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

1637 statements  

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

16from rest_framework import generics 

17from rest_framework import mixins 

18from rest_framework import status 

19from rest_framework.response import Response 

20 

21from django.conf import settings 

22from django.contrib.syndication.views import Feed 

23from django.core.cache import cache 

24from django.core.exceptions import SuspiciousOperation 

25from django.core.files.temp import NamedTemporaryFile 

26from django.core.paginator import EmptyPage 

27from django.core.paginator import Paginator 

28from django.db.models import F 

29from django.db.models import Q 

30from django.db.models import Value 

31from django.db.models.functions import Greatest 

32from django.http import Http404 

33from django.http import HttpResponse 

34from django.http import HttpResponseBadRequest 

35from django.http import HttpResponseRedirect 

36from django.http import HttpResponseServerError 

37from django.http import JsonResponse 

38from django.shortcuts import get_object_or_404 

39from django.shortcuts import render 

40from django.urls import reverse 

41from django.utils import timezone 

42from django.utils.decorators import method_decorator 

43from django.utils.translation import get_language 

44from django.utils.translation import gettext_lazy as _ 

45from django.views.decorators.csrf import csrf_exempt 

46from django.views.decorators.http import require_http_methods 

47from django.views.generic import TemplateView 

48from django.views.generic import View 

49 

50from comments_api.constants import PARAM_PREVIEW 

51from matching import crossref 

52from matching import matching 

53from ptf import citedby 

54from ptf import exceptions 

55from ptf import model_data 

56from ptf import model_data_converter 

57from ptf import model_helpers 

58from ptf import models 

59from ptf.cmds import ptf_cmds 

60from ptf.cmds import solr_cmds 

61from ptf.cmds import xml_cmds 

62from ptf.cmds.xml import xml_utils 

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

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

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

66from ptf.display import resolver 

67from ptf.display import utils 

68from ptf.models import Article 

69from ptf.serializers.serializers import AbstractSerializer 

70from ptf.serializers.serializers import CollectionSerializer 

71from ptf.serializers.serializers import ProviderSerializer 

72from ptf.serializers.serializers import SitesSerializer 

73from ptf.solr.search_helpers import CleanSearchURL 

74from ptf.utils import highlight_diff 

75 

76from .models import Collection 

77from .models import Provider 

78from .models import PtfSite 

79from .url_utils import format_url_with_params 

80 

81 

82def update_context_with_desc(context): 

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

84 context[ 

85 "online_first_desc_fr" 

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

87 context[ 

88 "online_first_desc_fr" 

89 ] += """<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>""" 

90 if not online_first_cr: 

91 context[ 

92 "online_first_desc_fr" 

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

94 

95 context[ 

96 "online_first_desc_en" 

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

98 context[ 

99 "online_first_desc_en" 

100 ] += """<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>""" 

101 if not online_first_cr: 

102 context[ 

103 "online_first_desc_en" 

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

105 

106 

107@require_http_methods(["POST"]) 

108def set_formula_display(request): 

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

110 

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

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

113 request.session["formula_display"] = formula_display 

114 

115 return HttpResponseRedirect(next) 

116 

117 

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

119# if not collection: 

120# raise Http404 

121# 

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

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

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

125# return thesis(request) 

126# else: 

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

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

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

130# request.GET = queryDict 

131# request.path = path 

132# request.path_info = path 

133# return sorted_books(request) 

134 

135 

136# @require_http_methods(["GET"]) 

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

138# cont = model_helpers.get_container(pid) 

139# return views_get_container(request, cont, api) 

140 

141 

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

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

144 raise Http404 

145 

146 articles_to_appear = is_cr = False 

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

148 context["articles_to_appear"] = articles_to_appear = True 

149 

150 if ( 

151 hasattr(settings, "SITE_NAME") 

152 and len(settings.SITE_NAME) == 6 

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

154 ): 

155 context["is_cr"] = is_cr = True 

156 

157 context["online_first_cr"] = False 

158 if container.with_online_first and is_cr: 

159 context["articles_to_appear"] = articles_to_appear = True 

160 context["online_first_cr"] = True 

161 

162 collection = container.my_collection 

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

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

165 

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

167 

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

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

170 

171 references = False 

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

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

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

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

176 references = True 

177 context["references"] = references 

178 

179 context["book"] = container 

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

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

182 

183 else: 

184 if articles_to_appear and is_cr: 

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

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

187 ) 

188 

189 else: 

190 articles = container.article_set.all() 

191 # TODO next_issue, previous_issue 

192 

193 context["issue"] = container 

194 context["articles"] = articles 

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

196 

197 # commun article ? 

198 context["coltype"] = collection.coltype 

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

200 relatedobject_ptr__resource__pk=container.pk, 

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

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

203 relatedobject_ptr__resource__pk=container.pk, 

204 ).filter(rel="review") 

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

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

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

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

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

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

211 

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

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

214 "-vseries_int", "-year", "-volume_int", "number_int" 

215 ) 

216 if qs.exists(): 

217 last_issue = qs.first() 

218 if container.pid == last_issue.pid: 

219 context["last_issue"] = True 

220 

221 context["bs_version"] = ( 

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

223 ) 

224 

225 update_context_with_desc(context) 

226 

227 

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

229# context = {} 

230# views_update_container_context(request, container, context) 

231# template = context['template'] 

232# return render(request, template, context) 

233 

234 

235# class ContainerView(TemplateView): 

236# template_name = '' 

237# 

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

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

240# 

241# if 'pid' in kwargs: 

242# pid = kwargs['pid'] 

243# else: 

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

245# container = model_helpers.get_container(pid) 

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

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

248# else: 

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

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

251# 

252# return context 

253 

254 

255# def views_get_article( 

256# request: HttpRequest, 

257# article: Optional[Article], 

258# api=False, 

259# lang=None, 

260# *args, 

261# **kwargs 

262# ): 

263# 

264# 

265# return render(request, template, context) 

266 

267 

268# @require_http_methods(["GET"]) 

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

270# return ItemView.as_view()(request) 

271# 

272# 

273# @require_http_methods(["GET"]) 

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

275# return ItemView.as_view()(request) 

276 

277 

278class ItemView(TemplateView): 

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

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

281 self.template_name = "" 

282 self.obj = None 

283 self.pid = None 

284 self.display_lang = None 

285 

286 def get_obj(self, **kwargs): 

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

288 if self.pid is None: 

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

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

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

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

293 raise Http404 

294 

295 if "/" in self.pid: 

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

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

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

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

300 

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

302 else: 

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

304 if obj is not None: 

305 obj = obj.cast() 

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

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

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

309 

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

311 raise Http404 

312 

313 self.obj = obj 

314 

315 def get_context_data(self, **kwargs): 

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

317 context["obj"] = self.obj 

318 return context 

319 

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

321 if "obj" in kwargs: 

322 self.obj = kwargs["obj"] 

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

324 self.display_lang = kwargs["lang"] 

325 

326 if ( 

327 self.obj is not None 

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

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

330 

331 self.get_obj(**kwargs) 

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

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

334 

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

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

337 kwargs["obj"] = self.obj 

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

339 kwargs["lang"] = self.display_lang 

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

341 

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

343 

344 

345class ArticleView(ItemView): 

346 def get_context_data(self, **kwargs): 

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

348 request = self.request 

349 display_lang = self.display_lang 

350 article = self.obj 

351 

352 article.title_tex = article.title_tex.strip() 

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

354 container = article.my_container 

355 collection = container.my_collection 

356 citing_articles = article.citations() 

357 

358 with_tex = ( 

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

360 ) 

361 

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

363 context["display_lang"] = display_lang 

364 else: 

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

366 

367 context["collection"] = collection 

368 context["is_translation"] = False 

369 context["full_translation"] = False 

370 context["needs_translation"] = True 

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

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

373 ) 

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

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

376 # langs.append(article.trans_lang) 

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

378 if translated_article.lang not in langs: 

379 langs.append(translated_article.lang) 

380 if display_lang == translated_article.lang: 

381 context["is_translation"] = True 

382 context["needs_translation"] = False 

383 context["translated_article"] = translated_article 

384 context["body_html"] = translated_article.body_html 

385 

386 context["languages"] = langs 

387 

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

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

390 ): 

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

392 context["body_html"] = article.body_html 

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

394 context["needs_translation"] = False 

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

396 if abstract.lang == display_lang: 

397 context["abstract"] = abstract 

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

399 context["article_title"] = ( 

400 article.trans_title_tex if with_tex else article.trans_title_html 

401 ) 

402 context["article_original_title"] = ( 

403 article.title_tex if with_tex else article.title_html 

404 ) 

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

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

407 context["body_html"] = article.body_html 

408 for abstract in article.get_abstracts(): 

409 if abstract.lang == display_lang: 

410 context["abstract"] = abstract 

411 else: 

412 context["full_translation"] = True 

413 context["article_original_title"] = ( 

414 article.title_tex if with_tex else article.title_html 

415 ) 

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

417 if display_lang == translated_article.lang: 

418 context["article_title"] = ( 

419 translated_article.title_tex if with_tex else translated_article.title_html 

420 ) 

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

422 

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

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

425 else: 

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

427 

428 if ( 428 ↛ 432line 428 didn't jump to line 432

429 hasattr(settings, "ISSUE_TO_APPEAR_PID") 

430 and settings.ISSUE_TO_APPEAR_PID == container.pid 

431 ): 

432 context["articles_to_appear"] = True 

433 

434 # Format comment URL if comments are activated 

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

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

437 query_params = {"redirect_url": current_url} 

438 # Forward some query parameters if present 

439 preview_id = request.GET.get(PARAM_PREVIEW) 

440 if preview_id: 

441 query_params[PARAM_PREVIEW] = preview_id 

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

443 if comment_reply_to: 

444 query_params["commentReplyTo"] = comment_reply_to 

445 

446 context["comments_url"] = format_url_with_params( 

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

448 ) 

449 

450 stop_at_volume = is_cr = False 

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

452 hasattr(settings, "SITE_NAME") 

453 and len(settings.SITE_NAME) == 6 

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

455 ): 

456 context["is_cr"] = is_cr = True 

457 year = int(container.year) 

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

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

460 ): 

461 qs = models.Container.objects.filter( 

462 my_collection__pid=collection.pid, year=year 

463 ).exclude(title_html="") 

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

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

466 stop_at_volume = True 

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

468 

469 context["online_first_cr"] = False 

470 if container.with_online_first and is_cr: 

471 context["articles_to_appear"] = True 

472 context["online_first_cr"] = True 

473 

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

475 context["article_show_body"] = True 

476 

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

478 article, container, collection, stop_at_volume 

479 ) 

480 

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

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

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

484 collection = collection_membership.collection 

485 container.my_collection = collection 

486 

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

488 context.update( 

489 { 

490 "article": article, 

491 "citing_articles": citing_articles, 

492 "source": source, 

493 } 

494 ) 

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

496 else: 

497 context.update( 

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

499 ) 

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

501 

502 site_entry = settings.SITE_REGISTER[settings.SITE_NAME] 

503 

504 licence = None 

505 try: 

506 year = int(article.my_container.year) 

507 except ValueError: 

508 year = 0 

509 

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

511 licences = site_entry["licences"] 

512 i = len(licences) - 1 

513 while not licence and i >= 0: 

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

515 licence = licences[i][1] 

516 i -= 1 

517 context["licence"] = licence 

518 

519 context["coltype"] = collection.coltype 

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

521 relatedobject_ptr__resource__pk=article.pk, 

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

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

524 relatedobject_ptr__resource__pk=article.pk, 

525 ).filter(rel="review") 

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

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

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

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

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

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

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

533 context["bs_version"] = ( 

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

535 ) 

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

537 

538 self.template_name = context["template"] 

539 

540 return context 

541 

542 

543class ContainerView(ItemView): 

544 def get_context_data(self, **kwargs): 

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

546 

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

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

549 else: 

550 container = self.obj 

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

552 raise Http404 

553 

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

555 

556 self.template_name = context["template"] 

557 

558 return context 

559 

560 

561class CollectionView(ItemView): 

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

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

564 self.obj = kwargs["obj"] 

565 

566 collection = self.obj 

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

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

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

570 return thesis(request, collection=collection) 

571 else: 

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

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

574 path, queryDict = CleanSearchURL.decode(url) 

575 request.GET = queryDict 

576 request.path = path 

577 request.path_info = path 

578 return sorted_books(request) 

579 

580 

581class VolumeDetailView(TemplateView): 

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

583 

584 def get_context_data(self, **kwargs): 

585 # TODO next_volume, previous_volume 

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

587 

588 context["group_issues"] = False 

589 is_cr = ( 

590 hasattr(settings, "SITE_NAME") 

591 and len(settings.SITE_NAME) == 6 

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

593 ) 

594 context["is_cr"] = is_cr 

595 

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

597 

598 if is_cr: 

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

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

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

602 ) 

603 

604 with_thematic = ( 

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

606 ) 

607 context["with_thematic"] = with_thematic 

608 

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

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

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

612 

613 # Ex: /volume/WBLN_2018__5/ 

614 # issues_articles = [] 

615 # for issue in issues: 

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

617 # fpage_int=Cast( 

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

619 # IntegerField() 

620 # ) 

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

622 # 

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

624 

625 context["issues_articles"] = issues_articles 

626 context["collection"] = collection 

627 context["coltype"] = collection.coltype 

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

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

630 ) 

631 

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

633 now = timezone.now() 

634 curyear = now.year 

635 

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

637 

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

639 

640 return context 

641 

642 

643class VolumeGeneralDetailView(TemplateView): 

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

645 

646 def get_context_data(self, **kwargs): 

647 # TODO next_volume, previous_volume 

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

649 

650 context["group_issues"] = False 

651 is_cr = ( 

652 hasattr(settings, "SITE_NAME") 

653 and len(settings.SITE_NAME) == 6 

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

655 ) 

656 context["is_cr"] = is_cr 

657 

658 issues_articles, collection = model_helpers.get_issues_in_volume( 

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

660 ) 

661 

662 if is_cr: 

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

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

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

666 ) 

667 

668 with_thematic = ( 

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

670 ) 

671 context["with_thematic"] = with_thematic 

672 

673 context["issues_articles"] = issues_articles 

674 context["collection"] = collection 

675 context["coltype"] = collection.coltype 

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

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

678 ) 

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

680 return context 

681 

682 

683class ItemViewClassFactory: 

684 views = { 

685 "article": ArticleView, 

686 "container": ContainerView, 

687 "collection": CollectionView, 

688 "volume": VolumeDetailView, 

689 } 

690 

691 def get_resource_view(self, classname): 

692 classname = classname.lower() 

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

694 return self.views[classname] 

695 return None 

696 

697 

698@require_http_methods(["GET"]) 

699def journals(request, api=False): 

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

701 

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

703 

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

705 

706 

707@require_http_methods(["GET"]) 

708def actas(request, api=False): 

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

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

711 

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

713 

714 

715@require_http_methods(["GET"]) 

716def books(request, api=False): 

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

718 

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

720 

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

722 

723 

724@require_http_methods(["GET"]) 

725def proceedings(request): 

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

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

728 

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

730 

731 

732class IssuesView(TemplateView): 

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

734 

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

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

737 

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

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

740 return HttpResponseRedirect(url) 

741 

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

743 

744 def get_context_data(self, **kwargs): 

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

746 

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

748 

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

750 raise Http404 

751 try: 

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

753 if "numdam.org" in current_edition: 

754 current_edition = None 

755 except models.ExtLink.DoesNotExist: 

756 current_edition = None 

757 

758 result = model_helpers.get_volumes_in_collection(journal) 

759 volume_count = result["volume_count"] 

760 collections = [] 

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

762 item = model_helpers.get_volumes_in_collection(ancestor) 

763 volume_count = max(0, volume_count) 

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

765 collections.append(item) 

766 

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

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

769 collections.append(result) 

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

771 collections.sort( 

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

773 reverse=True, 

774 ) 

775 

776 is_cr = False 

777 if ( 777 ↛ 782line 777 didn't jump to line 782

778 hasattr(settings, "SITE_NAME") 

779 and len(settings.SITE_NAME) == 6 

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

781 ): 

782 is_cr = True 

783 

784 display_with_titles = True 

785 i = 0 

786 while display_with_titles and i < len(collections): 

787 col = collections[i] 

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

789 display_with_titles = False 

790 else: 

791 j = 0 

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

793 while display_with_titles and j < len(volumes): 

794 vol = volumes[j] 

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

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

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

798 display_with_titles = issue.title_tex != "" 

799 j += 1 

800 i += 1 

801 

802 context.update( 

803 { 

804 "obj": journal, 

805 "journal": journal, 

806 "sorted_issues": result["sorted_issues"], 

807 "coltype": journal.coltype, 

808 "volume_count": volume_count, 

809 "max_width": result["max_width"], 

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

811 "show_issues": True, 

812 "is_cr": is_cr, 

813 "current_edition": current_edition, 

814 "collections": collections, 

815 "display_with_titles": display_with_titles, 

816 } 

817 ) 

818 update_context_with_desc(context) 

819 

820 return context 

821 

822 

823@require_http_methods(["GET"]) 

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

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

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

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

828 

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

830 

831 request.GET = queryDict 

832 request.path = path 

833 request.path_info = path 

834 

835 filters = [] 

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

837 

838 path = request.path 

839 search_path = path + "?" 

840 

841 try: 

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

843 except ValueError: 

844 page = 1 

845 

846 params = { 

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

848 "sorted_by": sorted_by, 

849 "order_by": order_by, 

850 "page": page, 

851 "filters": filters, 

852 "path": path, 

853 "search_path": search_path, 

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

855 "query": query, 

856 } 

857 

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

859 

860 

861def generic_books(request, coltype, params): 

862 rows = 20 

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

864 

865 params["rows"] = 20 

866 params["start"] = start 

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

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

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

870 else: 

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

872 

873 cmd = solr_cmds.solrInternalSearchCmd(params) 

874 results = cmd.do() 

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

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

877 

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

879 

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

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

882 has_one_collection_filter = False 

883 collection_title_filter = "" 

884 if "filters" in params: 

885 for filter_ in params["filters"]: 

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

887 has_one_collection_filter = ( 

888 True if not has_one_collection_filter else False 

889 ) 

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

891 

892 if has_one_collection_filter: 

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

894 if obj: 

895 book = obj.cast() 

896 container = book.get_container() 

897 collection = container.get_collection() 

898 if collection.title_tex == collection_title_filter: 

899 context["collection"] = collection 

900 else: 

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

902 if other_collection.title_tex == collection_title_filter: 

903 context["collection"] = other_collection 

904 

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

906 

907 context["query"] = params["query"] 

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

909 context["numdam"] = True 

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

911 

912 

913@require_http_methods(["GET"]) 

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

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

916 

917 request.GET = queryDict 

918 request.path = path 

919 request.path_info = path 

920 

921 filters = [] 

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

923 

924 path = request.path 

925 search_path = path + "?" 

926 

927 try: 

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

929 except ValueError: 

930 page = 1 

931 

932 params = { 

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

934 "sorted_by": sorted_by, 

935 "order_by": order_by, 

936 "page": page, 

937 "filters": filters, 

938 "path": path, 

939 "search_path": search_path, 

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

941 "query": query, 

942 } 

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

944 

945 

946@require_http_methods(["GET"]) 

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

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

949 

950 request.GET = queryDict 

951 request.path = path 

952 request.path_info = path 

953 

954 filters = [] 

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

956 

957 collection = model_helpers.get_collection("CJPS") 

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

959 sorted_by = "number" 

960 order_by = "desc" 

961 else: 

962 found = False 

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

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

965 pid = lectures.pop() 

966 collection = model_helpers.get_collection(pid) 

967 if ( 

968 collection is not None 

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

970 ): 

971 found = True 

972 sorted_by = "pid" 

973 order_by = "desc" 

974 

975 path = request.path 

976 search_path = path + "?" 

977 

978 try: 

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

980 except ValueError: 

981 page = 1 

982 

983 params = { 

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

985 "sorted_by": sorted_by, 

986 "order_by": order_by, 

987 "page": page, 

988 "filters": filters, 

989 "path": path, 

990 "search_path": search_path, 

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

992 "query": query, 

993 } 

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

995 

996 

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

998class SearchView(TemplateView): 

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

1000 GET = None 

1001 path = None 

1002 

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

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

1005 if query == "": 

1006 return HttpResponseRedirect(reverse("help")) 

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

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

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

1010 self.GET = queryDict 

1011 self.path = path 

1012 try: 

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

1014 except SuspiciousOperation: 

1015 result = HttpResponseBadRequest("Bad request") 

1016 

1017 return result 

1018 

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

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

1021 return HttpResponseRedirect(reverse("help")) 

1022 

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

1024 show_eprint = True if show_eprint == "on" else show_eprint 

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

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

1027 

1028 return HttpResponseRedirect(path) 

1029 

1030 def get_context_data(self, **kwargs): 

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

1032 

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

1034 eprint = False 

1035 

1036 try: 

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

1038 if "eprint" in parse: 

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

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

1041 except: 

1042 value = cache.get("eprint") 

1043 eprint = False if not value else value 

1044 finally: 

1045 cache.close() 

1046 

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

1048 

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

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

1051 

1052 search_path = self.path + "?" 

1053 qs = [] 

1054 

1055 keep_qs_in_display = True 

1056 i = 0 

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

1058 

1059 while qti: 

1060 if i > 0: 

1061 search_path += "&" 

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

1063 

1064 if qti == "date": 

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

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

1067 

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

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

1070 

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

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

1073 else: 

1074 if qti == "author_ref": 

1075 keep_qs_in_display = False 

1076 

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

1078 

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

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

1081 if noti == "on": 

1082 noti = True 

1083 else: 

1084 noti = False 

1085 

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

1087 

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

1089 

1090 if noti: 

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

1092 

1093 i += 1 

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

1095 

1096 try: 

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

1098 except ValueError: 

1099 page = 1 

1100 

1101 if len(qs) < 1: 

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

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

1104 raise SuspiciousOperation("Bad request") 

1105 

1106 rows = 20 

1107 start = (page - 1) * rows 

1108 

1109 params = { 

1110 "filters": filters, 

1111 "qs": qs, 

1112 "page": page, 

1113 "start": start, 

1114 "rows": rows, 

1115 "search_path": search_path, 

1116 "path": self.path, 

1117 } 

1118 

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

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

1121 params["sort"] = sort 

1122 

1123 cmd = solr_cmds.solrSearchCmd(params) 

1124 results = cmd.do() 

1125 

1126 if not keep_qs_in_display: 

1127 qs = [] 

1128 

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

1130 

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

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

1133 

1134 return context 

1135 

1136 

1137@require_http_methods(["GET"]) 

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

1139 """ 

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

1141 @param api: 

1142 @return: 

1143 """ 

1144 

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

1146 

1147 request.GET = queryDict 

1148 request.path = path 

1149 request.path_info = path 

1150 

1151 # to set default value on letter 

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

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

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

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

1156 letter = filter_[39] 

1157 

1158 try: 

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

1160 except ValueError: 

1161 page = 1 

1162 

1163 rows = 60 

1164 

1165 all_authors = model_helpers.get_authors_by_letter(letter) 

1166 paginator = Paginator(all_authors, rows) 

1167 try: 

1168 authors = paginator.page(page) 

1169 except EmptyPage: 

1170 raise Http404 

1171 

1172 context = { 

1173 "authors": authors, 

1174 "letters": string.ascii_uppercase, 

1175 "letter_active": letter, 

1176 "query": query, 

1177 } 

1178 

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

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

1181 

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

1183 

1184 

1185@require_http_methods(["GET"]) 

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

1187 resource = model_helpers.get_resource(aid) 

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

1189 raise Http404 

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

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

1192 item = resource.cast() 

1193 citing = item.citations() 

1194 context = {"resource": item} 

1195 context["citing"] = citing 

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

1197 

1198 

1199@require_http_methods(["GET"]) 

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

1201 """ 

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

1203 si l'item n'existe pas : 404 

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

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

1206 """ 

1207 

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

1209 

1210 

1211@require_http_methods(["GET"]) 

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

1213 """ 

1214 return tex file of a Resource if embargo not present 

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

1216 If there is an embargo redirect to the resource page 

1217 """ 

1218 

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

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

1221 

1222 type_extension = { 

1223 "pdf": "application/pdf", 

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

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

1226 "png": "image/png", 

1227 "jpg": "image/jpeg", 

1228 } 

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

1230 

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

1232 # the article/issue binary file 

1233 if not binary_file_type: 

1234 binary_file_type = "self" 

1235 

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

1237 resource = model_helpers.get_resource_by_doi(pid) 

1238 else: 

1239 resource = model_helpers.get_resource(pid) 

1240 

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

1242 resource = resource.cast() 

1243 

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

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

1246 

1247 

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

1249 """ 

1250 get the filename from the database 

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

1252 """ 

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

1254 return None, 404 

1255 

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

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

1258 return None, 404 

1259 

1260 filename = None 

1261 status = 200 

1262 

1263 try: 

1264 # May return None if there is an embargo 

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

1266 except exceptions.ResourceDoesNotExist: 

1267 status = 404 

1268 

1269 # File is protected 

1270 if filename is None: 

1271 status = 403 

1272 

1273 return filename, status 

1274 

1275 

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

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

1278 raise Http404 

1279 elif status == 403: 

1280 template_name = "403withRedirect.html" 

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

1282 else: 

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

1284 

1285 try: 

1286 file_ = open(full_filename, "rb") 

1287 except OSError: 

1288 print("cannot open ", full_filename) 

1289 raise Http404 

1290 else: 

1291 response = sendfile(request, full_filename) 

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

1293 file_.close() 

1294 return response 

1295 

1296 

1297########################################################################## 

1298# 

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

1300# 

1301########################################################################## 

1302class APILookupView(View): 

1303 def get(self, request): 

1304 title = request.GET["title"] 

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

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

1307 params = { 

1308 "qs": [ 

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

1310 ], 

1311 } 

1312 if year: 

1313 params["qs"].append( 

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

1315 ) 

1316 if authors: 

1317 params["qs"].append( 

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

1319 ) 

1320 cmd = solr_cmds.solrSearchCmd(params) 

1321 results = cmd.do() 

1322 if len(results.docs): 

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

1324 else: 

1325 data = {} 

1326 return JsonResponse(data) 

1327 

1328 

1329class APIFetchView(View): 

1330 def get(self, request): 

1331 data = "" 

1332 pid = request.GET["pid"] 

1333 if pid: 

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

1335 data = article.get_citation(with_formatting=True) 

1336 return HttpResponse(data) 

1337 

1338 

1339class ArticlesAPIView(View): 

1340 def get(self, request): 

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

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

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

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

1345 return JsonResponse( 

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

1347 ) 

1348 

1349 

1350class ArticleDumpAPIView(View): 

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

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

1353 

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

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

1356 else: 

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

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

1359 raise Http404 

1360 

1361 date_accepted = date_published = date_online_first = None 

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

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

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

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

1366 if article.date_published: 1366 ↛ 1368line 1366 didn't jump to line 1368

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

1368 date_received = ( 

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

1370 ) 

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

1372 

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

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

1375 authors = "; ".join(author_names) 

1376 else: 

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

1378 if author_names: 

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

1380 else: 

1381 authors = None 

1382 

1383 page_count = article.get_article_page_count() 

1384 

1385 result = { 

1386 "title_tex": article.title_tex, 

1387 "title_html": article.title_html, 

1388 "trans_title_tex": article.trans_title_tex, 

1389 "trans_title_html": article.trans_title_html, 

1390 "lang": article.lang, 

1391 "doi": article.doi or None, 

1392 "pid": article.pid, 

1393 "authors": authors, 

1394 "issue_pid": article.my_container.pid, 

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

1396 "volume": article.my_container.volume, 

1397 "number": article.my_container.number, 

1398 "year": article.my_container.year, 

1399 "issue_title": article.my_container.title_tex, 

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

1401 "date_accepted": date_accepted, 

1402 "date_online_first": date_online_first, 

1403 "date_published": date_published, 

1404 "date_received": date_received, 

1405 "date_revised": date_revised, 

1406 "page_count": page_count, 

1407 "body_html": article.body_html, 

1408 "pci_section": article.get_pci_section(), 

1409 } 

1410 

1411 extids = [] 

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

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

1414 result["extids"] = extids 

1415 

1416 result["kwds"] = [ 

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

1418 for kwd in article.kwd_set.all() 

1419 ] 

1420 

1421 awards = [] 

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

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

1424 result["awards"] = awards 

1425 

1426 abstracts = [] 

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

1428 abstracts.append( 

1429 { 

1430 "lang": abstract.lang, 

1431 "value_tex": abstract.value_tex, 

1432 "value_html": abstract.value_html, 

1433 } 

1434 ) 

1435 result["abstracts"] = abstracts 

1436 

1437 bibitems = [] 

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

1439 bibitems.append(bib.citation_html) 

1440 result["bibitems"] = bibitems 

1441 

1442 return JsonResponse(result) 

1443 

1444 

1445class BookDumpAPIView(View): 

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

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

1448 

1449 book = model_helpers.get_container(pid) 

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

1451 raise Http404 

1452 

1453 result = { 

1454 "title_tex": book.title_tex, 

1455 "title_html": book.title_html, 

1456 "doi": book.doi or "", 

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

1458 } 

1459 

1460 extids = [] 

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

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

1463 result["extids"] = extids 

1464 

1465 result["kwds"] = [ 

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

1467 ] 

1468 

1469 abstracts = [] 

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

1471 abstracts.append( 

1472 { 

1473 "lang": abstract.lang, 

1474 "value_tex": abstract.value_tex, 

1475 "value_html": abstract.value_html, 

1476 } 

1477 ) 

1478 result["abstracts"] = abstracts 

1479 

1480 bibitems = [] 

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

1482 bibitems.append(bib.citation_html) 

1483 result["bibitems"] = bibitems 

1484 

1485 return JsonResponse(result) 

1486 

1487 

1488class AllIssuesAPIView(View): 

1489 def get(self, request): 

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

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

1492 

1493 

1494class CollectionIssnAPIView(View): 

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

1496 collection = get_object_or_404( 

1497 models.Collection, 

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

1499 ) 

1500 if collection.parent: 

1501 url = ( 

1502 "" 

1503 if collection.parent.pid == collection.pid 

1504 else collection.parent.get_absolute_url() 

1505 ) 

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

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

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

1509 

1510 

1511class CollectionsAPIView(View): 

1512 def get(self, request): 

1513 collections_pids = list( 

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

1515 ) 

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

1517 

1518 

1519class CollectionAPIView( 

1520 generics.GenericAPIView, mixins.UpdateModelMixin, mixins.DestroyModelMixin 

1521): 

1522 """ 

1523 Endpoint for collections 

1524 """ 

1525 

1526 queryset = models.Collection.objects.all() 

1527 serializer_class = CollectionSerializer 

1528 lookup_field = "pid" 

1529 

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

1531 pid = kwargs.get("pid") 

1532 collection = get_object_or_404(models.Collection, pid=pid) 

1533 print(collection) 

1534 serializer = CollectionSerializer(collection) 

1535 serializer_data = serializer.data 

1536 sites = PtfSite.objects.all() 

1537 serializer_sites = SitesSerializer(sites, many=True) 

1538 abstract = collection.abstract_set.filter(tag="description") 

1539 serializer_abstract = AbstractSerializer(abstract, many=True) 

1540 providers = Provider.objects.all() 

1541 serializer_provider = ProviderSerializer(providers, many=True) 

1542 

1543 return JsonResponse( 

1544 { 

1545 "collection": serializer_data, 

1546 "sites": serializer_sites.data, 

1547 "descriptions": serializer_abstract.data, 

1548 "providers": serializer_provider.data, 

1549 } 

1550 ) 

1551 

1552 def patch(self, request, *args, **kwargs): 

1553 instance = get_object_or_404(Collection, *args, **kwargs) 

1554 print(instance) 

1555 identifiers = instance.resourceid_set.all() 

1556 identifiers.delete() 

1557 

1558 try: 

1559 sites_member = models.SiteMembership.objects.filter(resource_id=instance.id) 

1560 sites_member.delete() 

1561 

1562 for dict_extlink in request.data["extlink_set"]: 

1563 try: 

1564 ext_link = models.ExtLink.objects.get(id=dict_extlink["id"]) 

1565 ext_link.location = dict_extlink["location"] 

1566 ext_link.mimetype = dict_extlink["mimetype"] 

1567 ext_link.save() 

1568 except: 

1569 data = {} 

1570 data["location"] = dict_extlink["location"] 

1571 data["mimetype"] = dict_extlink["mimetype"] 

1572 data["rel"] = dict_extlink["rel"] 

1573 

1574 data["resource"] = instance.resource_ptr 

1575 ext_link = models.ExtLink.objects.create(**data) 

1576 

1577 except: 

1578 print("site member no set") 

1579 sites = request.data["sites"] 

1580 for site in sites: 

1581 try: 

1582 ptfSite = PtfSite.objects.get(pk=site["id"]) 

1583 if ptfSite is not None: 

1584 data = {} 

1585 data["site"] = ptfSite 

1586 data["resource_id"] = instance.id 

1587 models.SiteMembership.objects.create(**data) 

1588 except Exception: 

1589 pass 

1590 

1591 # instance.sitemembership_set.set([]) 

1592 

1593 if len(identifiers) > 0: 

1594 instance.save() 

1595 return self.partial_update(request, *args, **kwargs) 

1596 

1597 def delete(self, request, pid, format=None): 

1598 """ 

1599 

1600 @param request: 

1601 @param pid: 

1602 @param format: 

1603 @return: 

1604 """ 

1605 collection = model_helpers.get_collection(pid) 

1606 volumes = model_helpers.get_volumes_in_collection(collection) 

1607 results = volumes["sorted_issues"] 

1608 

1609 if len(results) > 0: 

1610 if len(results["volumes"]) > 0: 

1611 return Response(status=status.HTTP_304_NOT_MODIFIED) 

1612 else: 

1613 collection.delete() 

1614 return Response(status=status.HTTP_204_NO_CONTENT) 

1615 

1616 

1617class CollectionExportCSV(View): 

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

1619 colid = kwargs.get("colid") 

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

1621 

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

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

1624 

1625 csv_header = [ 

1626 "doi", 

1627 "title", 

1628 "date_accepted", 

1629 "date_first_publication", 

1630 ] 

1631 

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

1633 writer.writerow(csv_header) 

1634 

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

1636 my_container__my_collection__pid=colid 

1637 ).order_by("-date_accepted"): 

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

1639 first_online = ( 

1640 article.date_online_first 

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

1642 else article.date_published 

1643 ) 

1644 writer.writerow( 

1645 [ 

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

1647 xml_utils.normalise_span(article.title_tex), 

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

1649 if article.date_accepted is not None 

1650 else "", 

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

1652 ] 

1653 ) 

1654 

1655 return response 

1656 

1657 

1658class IssuesAPIView(View): 

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

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

1661 issues_pids = list( 

1662 models.Container.objects.filter( 

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

1664 ) 

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

1666 .order_by("pid") 

1667 ) 

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

1669 

1670 

1671class IssueListAPIView(View): 

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

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

1674 articles_pids = list( 

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

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

1677 .order_by("pid") 

1678 ) 

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

1680 

1681 

1682class ItemXMLView(View): 

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

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

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

1686 

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

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

1689 # The id given is a DOI 

1690 article = model_helpers.get_article_by_doi(pid) 

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

1692 pid = article.pid 

1693 else: 

1694 raise Http404 

1695 

1696 xml_body = ptf_cmds.exportPtfCmd( 

1697 { 

1698 "pid": pid, 

1699 "with_internal_data": False, 

1700 "with_binary_files": False, 

1701 "for_archive": True, 

1702 "full_xml": full_xml, 

1703 } 

1704 ).do() 

1705 

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

1707 

1708 

1709class ItemFileListAPIView(View): 

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

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

1712 

1713 resource = model_helpers.get_resource(pid) 

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

1715 raise Http404 

1716 

1717 obj = resource.cast() 

1718 binary_files = obj.get_binary_files_location() 

1719 

1720 result = {"files": binary_files} 

1721 

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

1723 articles = [] 

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

1725 article_files = article.get_binary_files_location() 

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

1727 result["articles"] = articles 

1728 

1729 return JsonResponse(result) 

1730 

1731 

1732class APIMatchOneView(View): 

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

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

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

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

1737 

1738 resource = model_helpers.get_resource(pid) 

1739 if not resource: 

1740 raise Http404 

1741 

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

1743 return HttpResponseBadRequest( 

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

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

1746 ) 

1747 result = "" 

1748 

1749 status = 200 

1750 message = "" 

1751 

1752 try: 

1753 obj = resource.cast() 

1754 seq = int(seq) 

1755 if seq == 0: 

1756 # Article, Book or Book part match 

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

1758 result = matching.match_article(obj, what) 

1759 else: 

1760 # TODO match book 

1761 pass 

1762 else: 

1763 bibitem = model_helpers.get_bibitem_by_seq(obj, seq) 

1764 if not bibitem: 

1765 raise Http404 

1766 result = matching.match_bibitem(bibitem, what) 

1767 

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

1769 except Timeout as exception: 

1770 return HttpResponse(exception, status=408) 

1771 except Exception as exception: 

1772 return HttpResponseServerError(exception) 

1773 

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

1775 return JsonResponse(data) 

1776 

1777 

1778class APIMatchAllView(View): 

1779 @staticmethod 

1780 def get_existing_ids(pid, what, force): 

1781 query_bibitemids = ( 

1782 models.BibItemId.objects.filter( 

1783 bibitem__resource__pid__contains=pid, 

1784 id_type__in=what, 

1785 ) 

1786 .select_related( 

1787 "bibitem", 

1788 "bibitem__resource", 

1789 ) 

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

1791 ) 

1792 query_extids = models.ExtId.objects.filter( 

1793 resource__pid__contains=pid, 

1794 id_type__in=what, 

1795 ).select_related("resource") 

1796 if force == "1": 

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

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

1799 if force == "2": 

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

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

1802 bibitemids = { 

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

1804 for bibitemid in query_bibitemids 

1805 } 

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

1807 return bibitemids.union(extids) 

1808 

1809 @staticmethod 

1810 def get_possible_ids(pid, what): 

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

1812 "resource" 

1813 ) 

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

1815 classname="TranslatedArticle" 

1816 ) 

1817 bibitemids = { 

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

1819 for type_ in what 

1820 for item in bibitems 

1821 if type_ != "pmid" 

1822 } 

1823 # we remove doi from possible extids 

1824 if "doi" in what: 

1825 what.remove("doi") 

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

1827 return bibitemids.union(extids) 

1828 

1829 @staticmethod 

1830 def delete_ids_if_necessary(pid, what, force): 

1831 if force == "1": 

1832 models.BibItemId.objects.filter( 

1833 bibitem__resource__pid__contains=pid, 

1834 id_type__in=what, 

1835 checked=False, 

1836 false_positive=False, 

1837 ).delete() 

1838 models.ExtId.objects.filter( 

1839 resource__pid__contains=pid, 

1840 id_type__in=what, 

1841 checked=False, 

1842 false_positive=False, 

1843 ).delete() 

1844 if force == "2": 

1845 models.BibItemId.objects.filter( 

1846 bibitem__resource__pid__contains=pid, id_type__in=what 

1847 ).delete() 

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

1849 

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

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

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

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

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

1855 what = what.split("_") 

1856 if force != "0": 

1857 self.delete_ids_if_necessary(pid, what, force) 

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

1859 possible = self.get_possible_ids(pid, what) 

1860 ids = list(possible - existing) 

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

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

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

1864 

1865 

1866class UpdateMatchingView(View): 

1867 resource = None 

1868 

1869 def post_update(self): 

1870 model_helpers.post_resource_updated(self.resource) 

1871 

1872 def update_obj(self, action): 

1873 if action == "delete": 

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

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

1876 elif action == "mark-checked": 

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

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

1879 elif action == "mark-unchecked": 

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

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

1882 

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

1884 cmd = xml_cmds.updateBibitemCitationXmlCmd() 

1885 cmd.set_bibitem(bibitem) 

1886 cmd.do() 

1887 

1888 self.post_update() 

1889 

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

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

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

1893 

1894 self.resource = model_helpers.get_resource(pid) 

1895 if not self.resource: 

1896 raise Http404 

1897 

1898 self.update_obj(action) 

1899 

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

1901 return HttpResponseRedirect(url) 

1902 

1903 

1904class UpdateExtIdView(View): 

1905 obj = None 

1906 resource = None 

1907 parent = None 

1908 

1909 def get_obj(self, pk): 

1910 try: 

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

1912 except models.ExtId.DoesNotExist: 

1913 raise Http404 

1914 

1915 self.obj = extid 

1916 self.resource = extid.resource 

1917 

1918 def post_update(self): 

1919 model_helpers.post_resource_updated(self.resource) 

1920 

1921 def update_obj(self, action): 

1922 if not self.obj: 

1923 raise Http404 

1924 

1925 if action == "delete": 

1926 self.obj.delete() 

1927 elif action == "toggle-checked": 

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

1929 self.obj.save() 

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

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

1932 self.obj.save() 

1933 

1934 self.post_update() 

1935 

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

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

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

1939 

1940 self.get_obj(pk) 

1941 self.update_obj(action) 

1942 

1943 if action == "delete": 

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

1945 return HttpResponseRedirect(url) 

1946 return JsonResponse({}) 

1947 

1948 

1949class UpdateBibItemIdView(UpdateExtIdView): 

1950 def get_obj(self, pk): 

1951 try: 

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

1953 except models.BibItemId.DoesNotExist: 

1954 raise Http404 

1955 

1956 self.obj = bibitemid 

1957 self.parent = bibitemid.bibitem 

1958 self.resource = bibitemid.bibitem.resource 

1959 

1960 def post_update(self): 

1961 cmd = xml_cmds.updateBibitemCitationXmlCmd() 

1962 cmd.set_bibitem(self.parent) 

1963 cmd.do() 

1964 

1965 model_helpers.post_resource_updated(self.resource) 

1966 

1967 

1968class APIFetchId(View): 

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

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

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

1972 pk = kwargs.get("pk") 

1973 resource = kwargs["resource"] 

1974 

1975 if what == "pmid": 

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

1977 return JsonResponse(data) 

1978 

1979 if not id_: 

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

1981 

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

1983 return HttpResponseBadRequest( 

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

1985 ) 

1986 

1987 try: 

1988 result = matching.fetch_id(id_, what) 

1989 except (IndexError, RequestException) as exception: 

1990 return HttpResponseServerError(exception) 

1991 

1992 if resource == "bibitemid": 

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

1994 raw = bibitem.citation_html 

1995 else: 

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

1997 raw = article.get_citation(request) 

1998 result = highlight_diff(raw, result) 

1999 

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

2001 return JsonResponse(data) 

2002 

2003 

2004class APIFetchAllView(View): 

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

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

2007 

2008 article = model_helpers.get_article(pid) 

2009 if not article: 

2010 raise Http404 

2011 

2012 ids = matching.get_all_fetch_ids(article) 

2013 

2014 data = {"ids": ids} 

2015 return JsonResponse(data) 

2016 

2017 

2018@require_http_methods(["GET"]) 

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

2020 template = "malsm.html" 

2021 context = { 

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

2023 "coltype": "book", 

2024 "template": template, 

2025 } 

2026 

2027 return render(request, template, context) 

2028 

2029 

2030class LatestArticlesFeed(Feed): 

2031 link = "" 

2032 ttl = 120 

2033 

2034 def get_feed(self, obj, request): 

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

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

2037 return feed 

2038 

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

2040 """ 

2041 Select the site whose RSS feed is requested. 

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

2043 """ 

2044 self.request = request 

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

2046 

2047 def title(self, obj): 

2048 if obj.requested_col_id: 

2049 collection = get_object_or_404( 

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

2051 ) 

2052 else: 

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

2054 return collection.title_sort 

2055 

2056 def description(self): 

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

2058 

2059 def items(self, obj): 

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

2061 if obj.requested_col_id: 

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

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

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

2065 )[:20] 

2066 

2067 def item_title(self, item): 

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

2069 

2070 def item_description(self, item): 

2071 language = get_language() 

2072 abstracts = item.get_abstracts() 

2073 if not abstracts: 

2074 return self.item_title(item) 

2075 try: 

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

2077 except models.Abstract.DoesNotExist: 

2078 return abstracts.first().value_html 

2079 

2080 def item_pubdate(self, item): 

2081 if item.date_published: 

2082 return item.date_published 

2083 return item.date_online_first 

2084 

2085 def item_link(self, item): 

2086 if item.doi: 

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

2088 return item.get_absolute_url() 

2089 

2090 

2091class LatestIssue(View): 

2092 def get(self, request): 

2093 colid = settings.COLLECTION_PID 

2094 if colid is None: 2094 ↛ 2095line 2094 didn't jump to line 2095, because the condition on line 2094 was never true

2095 return HttpResponse(status=404) 

2096 

2097 container_issues = ( 

2098 models.Container.objects.filter(my_collection__pid=colid) 

2099 .all() 

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

2101 ) 

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

2103 return HttpResponse(status=404) 

2104 

2105 container = container_issues.first() 

2106 url = container.get_absolute_url() 

2107 return HttpResponseRedirect(url) 

2108 

2109 

2110class RSSTemplate(TemplateView): 

2111 template_name = "blocks/syndication_feed.html" 

2112 

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

2114 # Numdam does not have an rss feed for now 

2115 if settings.SITE_ID == 3: 

2116 raise Http404 

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

2118 

2119 

2120class ArticleEditAPIView(View): 

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

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

2123 self.doi = None 

2124 self.colid = None 

2125 self.edit_all_fields = False 

2126 self.fields_to_update = ( 

2127 [] 

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

2129 

2130 @method_decorator(csrf_exempt) 

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

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

2133 

2134 def save_data(self, data_article): 

2135 pass 

2136 

2137 def restore_data(self, article): 

2138 pass 

2139 

2140 def convert_data_for_editor(self, data_article): 

2141 data_article.trans_title_formulas = [] 

2142 data_article.title_formulas = [] 

2143 data_article.abstract_formulas = [] 

2144 data_article.trans_abstract_formulas = [] 

2145 

2146 data_article.title_tex, data_article.trans_title_tex = get_tex_from_xml( 

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

2148 ) 

2149 

2150 for contrib in data_article.contributors: 

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

2152 

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

2154 data_article.abstracts = [ 

2155 { 

2156 "tag": "abstract", 

2157 "lang": data_article.lang, 

2158 "value_html": "", 

2159 "value_tex": "", 

2160 "value_xml": "", 

2161 } 

2162 ] 

2163 

2164 for abstract in data_article.abstracts: 

2165 if abstract["value_xml"]: 

2166 value_tex = get_tex_from_xml( 

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

2168 ) 

2169 else: 

2170 value_tex = "" 

2171 abstract["value_tex"] = value_tex 

2172 

2173 data_article.conference = ", ".join( 

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

2175 ) 

2176 data_article.topics = [ 

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

2178 ] 

2179 

2180 data_article.pci_section = "".join( 

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

2182 ) 

2183 

2184 data_article.subjs = [ 

2185 subj 

2186 for subj in data_article.subjs 

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

2188 ] 

2189 

2190 with_ordered_label = True 

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

2192 model_data_converter.convert_refdata_for_editor(ref) 

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

2194 with_ordered_label = False 

2195 data_article.bibitems_with_ordered_label = with_ordered_label 

2196 

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

2198 data_article.icon_url = None 

2199 if ext_link: 

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

2201 

2202 def convert_data_from_editor(self, data_article): 

2203 xtitle = CkeditorParser( 

2204 html_value=data_article.title_tex, 

2205 mml_formulas=data_article.title_formulas, 

2206 ignore_p=True, 

2207 ) 

2208 data_article.title_html = xtitle.value_html 

2209 data_article.title_tex = xtitle.value_tex 

2210 

2211 trans_title_xml = "" 

2212 if data_article.trans_title_tex: 

2213 xtranstitle = CkeditorParser( 

2214 html_value=data_article.trans_title_tex, 

2215 mml_formulas=data_article.trans_title_formulas, 

2216 ignore_p=True, 

2217 ) 

2218 data_article.trans_title_html = xtranstitle.value_html 

2219 data_article.trans_title_tex = xtranstitle.value_tex 

2220 trans_title_xml = xtranstitle.value_xml 

2221 

2222 data_article.title_xml = get_title_xml( 

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

2224 ) 

2225 

2226 for contrib in data_article.contributors: 

2227 contrib["addresses"] = [] 

2228 if "address_text" in contrib: 

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

2230 

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

2232 if i > 0: 

2233 xabstract = CkeditorParser( 

2234 html_value=abstract["value_tex"], 

2235 mml_formulas=data_article.trans_abstract_formulas, 

2236 ) 

2237 else: 

2238 xabstract = CkeditorParser( 

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

2240 ) 

2241 abstract["value_html"] = xabstract.value_html 

2242 abstract["value_tex"] = xabstract.value_tex 

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

2244 abstract[ 

2245 "value_xml" 

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

2247 else: 

2248 lang = abstract["lang"] 

2249 abstract[ 

2250 "value_xml" 

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

2252 

2253 data_article.subjs = [ 

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

2255 ] 

2256 if data_article.topics: 

2257 data_article.subjs.extend( 

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

2259 ) 

2260 if data_article.conference: 

2261 data_article.subjs.append( 

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

2263 ) 

2264 if data_article.pci_section: 

2265 data_article.subjs.append( 

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

2267 ) 

2268 

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

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

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

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

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

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

2275 ref["doi"] = doi 

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

2277 else: 

2278 ref["extids"] = [] 

2279 # URLs are in <comment> 

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

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

2282 # 'mimetype': '', 

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

2284 # 'base': '', 

2285 # 'metadata': ''}] 

2286 # else: 

2287 # ref['ext_links'] = [] 

2288 

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

2290 contribs = [] 

2291 for author_txt in author_array: 

2292 if author_txt: 

2293 lastname = firstname = "" 

2294 pos = author_txt.find(", ") 

2295 if pos > 0: 

2296 lastname = author_txt[0:pos] 

2297 firstname = author_txt[pos + 2 :] 

2298 else: 

2299 lastname = author_txt 

2300 

2301 contrib = model_data.create_contributor() 

2302 contrib["first_name"] = firstname 

2303 contrib["last_name"] = lastname 

2304 contrib["role"] = "author" 

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

2306 contribs.append(contrib) 

2307 ref["contributors"] = contribs 

2308 

2309 def replace_p(self, obj, key): 

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

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

2312 if pos > -1: 

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

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

2315 obj[key] = text 

2316 

2317 def replace_return(self, obj, key): 

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

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

2320 obj[key] = text 

2321 

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

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

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

2325 

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

2327 if not article: 

2328 raise Http404 

2329 

2330 data_article = model_data_converter.db_to_article_data(article) 

2331 self.convert_data_for_editor(data_article) 

2332 

2333 def obj_to_dict(obj): 

2334 return obj.__dict__ 

2335 

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

2337 

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

2339 

2340 def update_article(self, article_data, icon_file): 

2341 self.save_data(article_data) 

2342 

2343 collection = model_helpers.get_collection(self.colid) 

2344 

2345 model_data_converter.update_data_for_jats(article_data) 

2346 

2347 issue = None 

2348 existing_article = model_helpers.get_article_by_doi(article_data.doi) 

2349 new_data_article = None 

2350 

2351 if existing_article is not None: 

2352 issue = existing_article.my_container 

2353 new_data_article = model_data_converter.db_to_article_data(existing_article) 

2354 else: 

2355 new_data_article = model_data.create_articledata() 

2356 new_data_article.pid = article_data.pid 

2357 new_data_article.doi = article_data.doi 

2358 

2359 if self.edit_all_fields: 

2360 new_data_article = article_data 

2361 else: 

2362 for field in self.fields_to_update: 

2363 value = getattr(article_data, field) 

2364 setattr(new_data_article, field, value) 

2365 

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

2367 # New icon 

2368 if icon_file and issue: 

2369 relative_file_name = resolver.copy_file_obj_to_article_folder( 

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

2371 ) 

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

2373 if ext_link is None: 

2374 ext_link = model_data.create_extlink() 

2375 ext_link["rel"] = "icon" 

2376 ext_link["location"] = relative_file_name 

2377 new_data_article.ext_links.append(ext_link) 

2378 

2379 # No or removed icon 

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

2381 new_data_article.ext_links = [ 

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

2383 ] 

2384 

2385 cmd = xml_cmds.addArticleXmlCmd( 

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

2387 ) 

2388 cmd.set_collection(collection) 

2389 article = cmd.do() 

2390 

2391 self.restore_data(article) 

2392 

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

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

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

2396 

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

2398 body = json.loads(body_unicode) 

2399 

2400 article_data = Munch(body) 

2401 new_bibitems = [] 

2402 for bib in article_data.bibitems: 

2403 new_bibitems.append(Munch(bib)) 

2404 article_data.bibitems = new_bibitems 

2405 

2406 new_relations = [] 

2407 for relation in article_data.relations: 

2408 new_relations.append(Munch(relation)) 

2409 article_data.relations = new_relations 

2410 

2411 new_translations = [] 

2412 for translation in article_data.translations: 

2413 new_translations.append(Munch(translation)) 

2414 article_data.translations = new_translations 

2415 

2416 self.convert_data_from_editor(article_data) 

2417 

2418 icon_file = None 

2419 if "icon" in request.FILES: 

2420 icon_file = request.FILES["icon"] 

2421 

2422 self.update_article(article_data, icon_file) 

2423 

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

2425 

2426 

2427class CollectionEditAPIView(View): 

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

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

2430 self.colid = None 

2431 self.collection = None 

2432 self.edit_all_fields = True 

2433 self.fields_to_update = ( 

2434 [] 

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

2436 

2437 @method_decorator(csrf_exempt) 

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

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

2440 

2441 def save_data(self, data): 

2442 pass 

2443 

2444 def restore_data(self, collection): 

2445 pass 

2446 

2447 def convert_data_for_editor(self, data): 

2448 data.title, data.trans_title = get_tex_from_xml( 

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

2450 ) 

2451 # TODO: move periods inside GDML 

2452 data.periods = [] 

2453 

2454 def convert_data_from_editor(self, data): 

2455 xtitle = CkeditorParser(html_value=data.title, mml_formulas=[], ignore_p=True) 

2456 data.title_html = xtitle.value_html 

2457 data.title_tex = xtitle.value_tex 

2458 

2459 # TODO: get_title_xml for Collections 

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

2461 

2462 ids = [] 

2463 if data.issn: 

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

2465 if data.e_issn: 

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

2467 data.ids = ids 

2468 

2469 def replace_p(self, obj, key): 

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

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

2472 if pos > -1: 

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

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

2475 obj[key] = text 

2476 

2477 def replace_return(self, obj, key): 

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

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

2480 obj[key] = text 

2481 

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

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

2484 

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

2486 if not collection: 

2487 raise Http404 

2488 

2489 data = model_data_converter.db_to_publication_data(collection) 

2490 self.convert_data_for_editor(data) 

2491 

2492 def obj_to_dict(obj): 

2493 return obj.__dict__ 

2494 

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

2496 

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

2498 

2499 def update_collection(self, data): 

2500 self.save_data(data) 

2501 

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

2503 if existing_collection is None: 

2504 cls = ptf_cmds.addCollectionPtfCmd 

2505 new_data = data 

2506 else: 

2507 cls = ptf_cmds.updateCollectionPtfCmd 

2508 

2509 if self.edit_all_fields: 

2510 new_data = data 

2511 else: 

2512 new_data = model_data_converter.db_to_publication_data(existing_collection) 

2513 

2514 for field in self.fields_to_update: 

2515 value = getattr(data, field) 

2516 setattr(new_data, field, value) 

2517 

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

2519 cmd = cls(params) 

2520 if new_data.provider: 

2521 provider = model_helpers.get_provider_by_name(new_data.provider) 

2522 else: 

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

2524 cmd.set_provider(provider) 

2525 collection = cmd.do() 

2526 

2527 # TODO: Move xml_cmds add_objects_with_location inside ptf_cmds 

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

2529 

2530 self.restore_data(collection) 

2531 

2532 return collection 

2533 

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

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

2536 

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

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

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

2540 body = json.loads(body_unicode) 

2541 

2542 data = Munch(body) 

2543 self.convert_data_from_editor(data) 

2544 

2545 self.update_collection(data) 

2546 

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

2548 

2549 

2550class ArticleCitedByView(View): 

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

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

2553 

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

2555 resource = model_helpers.get_resource_by_doi(doi) 

2556 else: 

2557 resource = model_helpers.get_resource(doi) 

2558 

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

2560 raise Http404 

2561 

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

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

2564 status_code = 200 

2565 else: 

2566 status_code = 204 

2567 

2568 data = { 

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

2570 "status": status_code, 

2571 } 

2572 return JsonResponse(data) 

2573 

2574 

2575def export_citation(request, **kwargs): 

2576 data = "" 

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

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

2579 

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

2581 resource = model_helpers.get_resource_by_doi(pid) 

2582 else: 

2583 resource = model_helpers.get_resource(pid) 

2584 

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

2586 return HttpResponse(status=404) 

2587 

2588 if hasattr(resource, "article"): 

2589 document = resource.article 

2590 else: 

2591 document = model_helpers.get_container(resource.pid) 

2592 

2593 filename = "citation." + ext 

2594 if ext == "bib": 

2595 data = document.get_bibtex(request) 

2596 elif ext == "ris": 

2597 data = document.get_ris(request) 

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

2599 data = document.get_endnote(request) 

2600 

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

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

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

2604 else: 

2605 response = HttpResponse(status=204) 

2606 return response 

2607 

2608 

2609def get_tokenized(body): 

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

2611 math_symbols = {} 

2612 i = 0 

2613 

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

2615 if tag.name == "span": 

2616 math_symbols[i] = tag.decode_contents() 

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

2618 i += 1 

2619 # else: 

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

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

2622 # j += 1 

2623 

2624 body = str(soup) 

2625 

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

2627 

2628 

2629def detokenize(body, tokens): 

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

2631 

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

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

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

2635 id = int(id_list[0]) 

2636 if id in tokens["math"]: 

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

2638 

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

2640 

2641 return body 

2642 

2643 

2644class ExportArticleHtml(View): 

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

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

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

2648 doi = doi[:-1] 

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

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

2651 body = a.body_html 

2652 pid = a.__str__() 

2653 

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

2655 # tokenize = int(tokenize) 

2656 

2657 tokens_infos = get_tokenized(body) 

2658 body = tokens_infos["body"] 

2659 # tokens = tokens_infos["tokens"] 

2660 

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

2662 html_article.write(body) 

2663 html_article.seek(0) 

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

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

2666 

2667 return response 

2668 

2669 

2670class RecentArticlesPublished(View): 

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

2672 # - titre de l'article 

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

2674 # - collection de l'article 

2675 # - doi 

2676 # - Infos sur l'article 

2677 # - url 

2678 

2679 def get(self, request): 

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

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

2682 )[:100] 

2683 articles_infos = [] 

2684 container_pids = [] 

2685 nb_articles = articles.count() 

2686 i = 0 

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

2688 article = articles[i] 

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

2690 container_pids.append(article.my_container.pid) 

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

2692 item = { 

2693 "title": article.title_html, 

2694 "authors": article.get_authors_short(), 

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

2696 "doi": article.doi, 

2697 "citation_source": article.get_citation_base(), 

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

2699 } 

2700 articles_infos.append(item) 

2701 i += 1 

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

2703 

2704 

2705def get_first_n_authors(authors, n=3): 

2706 if len(authors) > n: 

2707 authors = authors[0:n] 

2708 authors.append("...") 

2709 return "; ".join(authors) 

2710 

2711 

2712def get_suggested_articles(article): 

2713 documents = [] 

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

2715 solr_cmds.auto_suggest_doi(obj, article) 

2716 

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

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

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

2720 dois = [ 

2721 doi 

2722 for doi in obj.doi_list.split() 

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

2724 ] 

2725 for doi in dois: 

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

2727 doc = {} 

2728 base_url = "" 

2729 if suggest: 

2730 doc = vars(suggest) 

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

2732 if suggest.my_container: 

2733 collection = suggest.my_container.my_collection 

2734 base_url = collection.website() or "" 

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

2736 doc["journal_abbrev"] = collection.abbrev 

2737 else: 

2738 try: 

2739 doc = crossref.crossref_request(doi) 

2740 except: 

2741 continue 

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

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

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

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

2746 

2747 if doc: 

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

2749 if base_url and suggest: 

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

2751 else: 

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

2753 documents.append(doc) 

2754 return documents 

2755 

2756 

2757@csrf_exempt 

2758def update_suggest(request, doi): 

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

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

2761 if not article: 

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

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

2764 obj.resource_doi = article.doi 

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

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

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

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

2769 obj.save() 

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

2771 

2772 

2773@csrf_exempt 

2774def graphical_abstract(request, doi): 

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

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

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

2778 obj.resource_doi = article.doi 

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

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

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

2782 obj.save() 

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

2784 elif request.method == "DELETE": 

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

2786 if obj: 

2787 obj.delete() 

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

2789 

2790 

2791class MoreLikeThisView(View): 

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

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

2794 article = model_helpers.get_resource_by_doi(doi) 

2795 if not article: 

2796 raise Http404 

2797 

2798 results = solr_cmds.research_more_like_this(article) 

2799 return JsonResponse(results)