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

1587 statements  

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

1import csv 

2import html 

3import json 

4import os 

5import re 

6import string 

7import urllib.parse 

8from itertools import chain 

9from operator import itemgetter 

10 

11from bs4 import BeautifulSoup 

12from django_sendfile import sendfile 

13from munch import Munch 

14from requests.exceptions import RequestException 

15from requests.exceptions import Timeout 

16 

17from django.conf import settings 

18from django.contrib.syndication.views import Feed 

19from django.core.cache import cache 

20from django.core.exceptions import SuspiciousOperation 

21from django.core.files.temp import NamedTemporaryFile 

22from django.core.paginator import EmptyPage 

23from django.core.paginator import Paginator 

24from django.db.models import F 

25from django.db.models import Q 

26from django.db.models import Value 

27from django.db.models.functions import Greatest 

28from django.http import Http404 

29from django.http import HttpResponse 

30from django.http import HttpResponseBadRequest 

31from django.http import HttpResponseRedirect 

32from django.http import HttpResponseServerError 

33from django.http import JsonResponse 

34from django.shortcuts import get_object_or_404 

35from django.shortcuts import render 

36from django.urls import reverse 

37from django.utils import timezone 

38from django.utils.decorators import method_decorator 

39from django.utils.translation import get_language 

40from django.utils.translation import gettext_lazy as _ 

41from django.views.decorators.csrf import csrf_exempt 

42from django.views.decorators.http import require_http_methods 

43from django.views.generic import TemplateView 

44from django.views.generic import View 

45 

46from comments_api.constants import PARAM_PREVIEW 

47from matching import crossref 

48from matching import matching 

49from ptf import citedby 

50from ptf import exceptions 

51from ptf import model_data 

52from ptf import model_data_converter 

53from ptf import model_helpers 

54from ptf import models 

55from ptf.cmds import ptf_cmds 

56from ptf.cmds import solr_cmds 

57from ptf.cmds import xml_cmds 

58from ptf.cmds.xml import xml_utils 

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

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

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

62from ptf.display import resolver 

63from ptf.display import utils 

64from ptf.models import Article 

65from ptf.solr.search_helpers import CleanSearchURL 

66from ptf.utils import highlight_diff 

67from ptf.views.components import breadcrumb 

68 

69from .url_utils import format_url_with_params 

70 

71# from django.utils.crypto import md5 

72# from django.utils import cache 

73# 

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

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

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

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

78# key_prefix, 

79# method, 

80# url.hexdigest(), 

81# ) 

82# return cache_key 

83# 

84# 

85# def ptf_generate_cache_header_key(key_prefix, request): 

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

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

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

89# key_prefix, 

90# url.hexdigest(), 

91# ) 

92# return cache_key 

93# 

94# 

95# cache._generate_cache_key = ptf_generate_cache_key 

96# cache._generate_cache_header_key = ptf_generate_cache_header_key 

97 

98 

99def update_context_with_desc(context): 

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

101 context[ 

102 "online_first_desc_fr" 

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

104 context[ 

105 "online_first_desc_fr" 

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

107 if not online_first_cr: 

108 context[ 

109 "online_first_desc_fr" 

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

111 

112 context[ 

113 "online_first_desc_en" 

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

115 context[ 

116 "online_first_desc_en" 

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

118 if not online_first_cr: 

119 context[ 

120 "online_first_desc_en" 

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

122 

123 

124@require_http_methods(["POST"]) 

125def set_formula_display(request): 

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

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

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

129 request.session["formula_display"] = formula_display 

130 # redis session needs to save item in state 

131 request.session.save() 

132 

133 # to read session with redis-django-sessions 

134 # rdata = request.session.load() 

135 return HttpResponseRedirect(next) 

136 

137 

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

139# if not collection: 

140# raise Http404 

141# 

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

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

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

145# return thesis(request) 

146# else: 

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

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

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

150# request.GET = queryDict 

151# request.path = path 

152# request.path_info = path 

153# return sorted_books(request) 

154 

155 

156# @require_http_methods(["GET"]) 

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

158# cont = model_helpers.get_container(pid) 

159# return views_get_container(request, cont, api) 

160 

161 

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

163 if container is None: 

164 raise Http404 

165 

166 articles_to_appear = is_cr = False 

167 if hasattr(settings, "ISSUE_TO_APPEAR_PID") and settings.ISSUE_TO_APPEAR_PID == container.pid: 

168 context["articles_to_appear"] = articles_to_appear = True 

169 

170 if ( 

171 hasattr(settings, "SITE_NAME") 

172 and len(settings.SITE_NAME) == 6 

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

174 ): 

175 context["is_cr"] = is_cr = True 

176 

177 context["online_first_cr"] = False 

178 if container.with_online_first and is_cr: 

179 context["articles_to_appear"] = articles_to_appear = True 

180 context["online_first_cr"] = True 

181 

182 collection = container.my_collection 

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

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

185 

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

187 context["btn_show_tex"] = settings.SHOW_TEX if hasattr(settings, "SHOW_TEX") else False 

188 

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

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

191 

192 references = False 

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

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

195 for art in container.article_set.all(): 

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

197 references = True 

198 context["references"] = references 

199 

200 context["book"] = container 

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

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

203 

204 else: 

205 if articles_to_appear and is_cr: 

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

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

208 ) 

209 

210 else: 

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

212 # TODO next_issue, previous_issue 

213 

214 context["issue"] = container 

215 context["articles"] = articles 

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

217 

218 # commun article ? 

219 context["coltype"] = collection.coltype 

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

221 relatedobject_ptr__resource__pk=container.pk, 

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

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

224 relatedobject_ptr__resource__pk=container.pk, 

225 ).filter(rel="review") 

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

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

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

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

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

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

232 

233 if collection.pid == "ART": 

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

235 "-vseries_int", "-year", "-volume_int", "number_int" 

236 ) 

237 if qs.exists(): 

238 last_issue = qs.first() 

239 if container.pid == last_issue.pid: 

240 context["last_issue"] = True 

241 

242 context["bs_version"] = ( 

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

244 ) 

245 

246 update_context_with_desc(context) 

247 

248 

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

250# context = {} 

251# views_update_container_context(request, container, context) 

252# template = context['template'] 

253# return render(request, template, context) 

254 

255 

256# class ContainerView(TemplateView): 

257# template_name = '' 

258# 

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

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

261# 

262# if 'pid' in kwargs: 

263# pid = kwargs['pid'] 

264# else: 

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

266# container = model_helpers.get_container(pid) 

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

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

269# else: 

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

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

272# 

273# return context 

274 

275 

276# def views_get_article( 

277# request: HttpRequest, 

278# article: Optional[Article], 

279# api=False, 

280# lang=None, 

281# *args, 

282# **kwargs 

283# ): 

284# 

285# 

286# return render(request, template, context) 

287 

288 

289# @require_http_methods(["GET"]) 

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

291# return ItemView.as_view()(request) 

292# 

293# 

294# @require_http_methods(["GET"]) 

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

296# return ItemView.as_view()(request) 

297 

298 

299class ItemView(TemplateView): 

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

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

302 self.template_name = "" 

303 self.obj = None 

304 self.pid = None 

305 self.display_lang = None 

306 

307 def get_obj(self, **kwargs): 

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

309 if self.pid is None: 

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

311 if self.pid is None: 

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

313 if "\x00" in self.pid: 

314 raise Http404 

315 

316 if "/" in self.pid: 

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

318 if not self.display_lang and self.pid[-3] == "-" and not self.pid[-2:].isdigit(): 

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

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

321 

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

323 else: 

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

325 if obj is not None: 

326 obj = obj.cast() 

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

328 if fct and callable(fct): 

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

330 

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

332 raise Http404 

333 

334 self.obj = obj 

335 

336 def get_context_data(self, **kwargs): 

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

338 context["journal"] = self.obj.get_collection() 

339 context["obj"] = self.obj 

340 return context 

341 

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

343 if "obj" in kwargs: 

344 self.obj = kwargs["obj"] 

345 if "lang" in kwargs: 

346 self.display_lang = kwargs["lang"] 

347 

348 if ( 

349 self.obj is not None 

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

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

352 

353 self.get_obj(**kwargs) 

354 if not self.display_lang: 

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

356 

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

358 if view_cls is not None: 

359 kwargs["obj"] = self.obj 

360 if self.display_lang is not None: 

361 kwargs["lang"] = self.display_lang 

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

363 

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

365 

366 

367class ArticleView(ItemView): 

368 def get_context_data(self, **kwargs): 

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

370 request = self.request 

371 display_lang = self.display_lang 

372 article = self.obj 

373 

374 article.title_tex = article.title_tex.strip() 

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

376 container = article.my_container 

377 collection = container.my_collection 

378 citing_articles = article.citations() 

379 

380 with_tex = ( 

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

382 ) 

383 

384 if display_lang is not None and len(display_lang) == 2: 

385 context["display_lang"] = display_lang 

386 else: 

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

388 

389 context["collection"] = collection 

390 context["journal"] = collection 

391 context["is_translation"] = False 

392 context["full_translation"] = False 

393 context["needs_translation"] = True 

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

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

396 ) 

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

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

399 # langs.append(article.trans_lang) 

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

401 if translated_article.lang not in langs: 

402 langs.append(translated_article.lang) 

403 if display_lang == translated_article.lang: 

404 context["is_translation"] = True 

405 context["needs_translation"] = False 

406 context["translated_article"] = translated_article 

407 context["body_html"] = translated_article.body_html 

408 

409 context["languages"] = langs 

410 

411 if display_lang == article.lang or ( 

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

413 ): 

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

415 context["body_html"] = article.body_html 

416 if display_lang == article.lang: 

417 context["needs_translation"] = False 

418 for abstract in article.get_abstracts(): 

419 if abstract.lang == display_lang: 

420 context["abstract"] = abstract 

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

422 context["article_title"] = ( 

423 article.trans_title_tex if with_tex else article.trans_title_html 

424 ) 

425 context["article_original_title"] = ( 

426 article.title_tex if with_tex else article.title_html 

427 ) 

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

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

430 context["body_html"] = article.body_html 

431 for abstract in article.get_abstracts(): 

432 if abstract.lang == display_lang: 

433 context["abstract"] = abstract 

434 else: 

435 context["full_translation"] = True 

436 context["article_original_title"] = ( 

437 article.title_tex if with_tex else article.title_html 

438 ) 

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

440 if display_lang == translated_article.lang: 

441 context["article_title"] = ( 

442 translated_article.title_tex if with_tex else translated_article.title_html 

443 ) 

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

445 

446 if context["is_translation"]: 

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

448 else: 

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

450 

451 if ( 

452 hasattr(settings, "ISSUE_TO_APPEAR_PID") 

453 and settings.ISSUE_TO_APPEAR_PID == container.pid 

454 ): 

455 context["articles_to_appear"] = True 

456 

457 # Format comment URL if comments are activated 

458 if getattr(settings, "COMMENTS_VIEWS_ARTICLE_COMMENTS", False) is True: 

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

460 query_params = {"redirect_url": current_url} 

461 # Forward some query parameters if present 

462 preview_id = request.GET.get(PARAM_PREVIEW) 

463 if preview_id: 

464 query_params[PARAM_PREVIEW] = preview_id 

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

466 if comment_reply_to: 

467 query_params["commentReplyTo"] = comment_reply_to 

468 

469 context["comments_url"] = format_url_with_params( 

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

471 ) 

472 

473 stop_at_volume = is_cr = False 

474 if ( 

475 hasattr(settings, "SITE_NAME") 

476 and len(settings.SITE_NAME) == 6 

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

478 ): 

479 context["is_cr"] = is_cr = True 

480 year = int(container.year) 

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

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

483 ): 

484 qs = models.Container.objects.filter( 

485 my_collection__pid=collection.pid, year=year 

486 ).exclude(title_html="") 

487 if qs.count() == 0: 

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

489 stop_at_volume = True 

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

491 

492 context["online_first_cr"] = False 

493 if container.with_online_first and is_cr: 

494 context["articles_to_appear"] = True 

495 context["online_first_cr"] = True 

496 

497 if hasattr(settings, "SHOW_BODY") and settings.SHOW_BODY: 

498 context["article_show_body"] = True 

499 

500 context["breadcrumb"] = breadcrumb.get_breadcrumb( 

501 article, container, collection, stop_at_volume 

502 ) 

503 context["btn_show_tex"] = settings.SHOW_TEX if hasattr(settings, "SHOW_TEX") else False 

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

505 if collection.pid == "MBK": 

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

507 collection = collection_membership.collection 

508 container.my_collection = collection 

509 

510 if container.ctype == "issue": 

511 context.update( 

512 { 

513 "article": article, 

514 "citing_articles": citing_articles, 

515 "source": source, 

516 } 

517 ) 

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

519 else: 

520 context.update( 

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

522 ) 

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

524 

525 site_entry = settings.SITE_REGISTER[settings.SITE_NAME] 

526 

527 licence = None 

528 try: 

529 year = int(article.my_container.year) 

530 except ValueError: 

531 year = 0 

532 

533 if "licences" in site_entry: 

534 licences = site_entry["licences"] 

535 i = len(licences) - 1 

536 while not licence and i >= 0: 

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

538 licence = licences[i][1] 

539 i -= 1 

540 context["licence"] = licence 

541 

542 context["coltype"] = collection.coltype 

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

544 relatedobject_ptr__resource__pk=article.pk, 

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

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

547 relatedobject_ptr__resource__pk=article.pk, 

548 ).filter(rel="review") 

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

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

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

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

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

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

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

556 context["bs_version"] = ( 

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

558 ) 

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

560 

561 self.template_name = context["template"] 

562 

563 return context 

564 

565 

566class ContainerView(ItemView): 

567 def get_context_data(self, **kwargs): 

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

569 

570 if self.pid == getattr(settings, "ISSUE_TO_APPEAR_PID", "XXX") and not self.obj: 

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

572 else: 

573 container = self.obj 

574 if container is None: 

575 raise Http404 

576 

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

578 

579 self.template_name = context["template"] 

580 

581 return context 

582 

583 

584class CollectionView(ItemView): 

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

586 if "obj" in kwargs: 

587 self.obj = kwargs["obj"] 

588 

589 collection = self.obj 

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

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

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

593 return thesis(request, collection=collection) 

594 else: 

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

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

597 path, queryDict = CleanSearchURL.decode(url) 

598 request.GET = queryDict 

599 request.path = path 

600 request.path_info = path 

601 return sorted_books(request) 

602 

603 

604class VolumeDetailView(TemplateView): 

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

606 

607 def get_context_data(self, **kwargs): 

608 # TODO next_volume, previous_volume 

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

610 

611 context["group_issues"] = False 

612 is_cr = ( 

613 hasattr(settings, "SITE_NAME") 

614 and len(settings.SITE_NAME) == 6 

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

616 ) 

617 context["is_cr"] = is_cr 

618 

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

620 year = issues_articles[0]["issue"].year.split("-") 

621 if len(year) > 1: 

622 year = int(year[0]) 

623 else: 

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

625 context["year"] = year 

626 

627 if is_cr: 

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

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

630 ) 

631 

632 with_thematic = ( 

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

634 ) 

635 context["with_thematic"] = with_thematic 

636 

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

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

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

640 

641 # Ex: /volume/WBLN_2018__5/ 

642 # issues_articles = [] 

643 # for issue in issues: 

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

645 # fpage_int=Cast( 

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

647 # IntegerField() 

648 # ) 

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

650 # 

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

652 

653 context["issues_articles"] = issues_articles 

654 context["collection"] = collection 

655 context["journal"] = collection 

656 context["coltype"] = collection.coltype 

657 context["breadcrumb"] = breadcrumb.get_breadcrumb( 

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

659 ) 

660 context["btn_show_tex"] = settings.SHOW_TEX if hasattr(settings, "SHOW_TEX") else False 

661 

662 if collection.pid == "ART": 

663 now = timezone.now() 

664 curyear = now.year 

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

666 

667 return context 

668 

669 

670class VolumeGeneralDetailView(TemplateView): 

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

672 

673 def get_context_data(self, **kwargs): 

674 # TODO next_volume, previous_volume 

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

676 

677 context["group_issues"] = False 

678 is_cr = ( 

679 hasattr(settings, "SITE_NAME") 

680 and len(settings.SITE_NAME) == 6 

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

682 ) 

683 context["is_cr"] = is_cr 

684 

685 issues_articles, collection = model_helpers.get_issues_in_volume( 

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

687 ) 

688 

689 if is_cr: 

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

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

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

693 ) 

694 

695 with_thematic = ( 

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

697 ) 

698 context["with_thematic"] = with_thematic 

699 

700 context["issues_articles"] = issues_articles 

701 context["collection"] = collection 

702 context["journal"] = collection 

703 context["coltype"] = collection.coltype 

704 context["breadcrumb"] = breadcrumb.get_breadcrumb( 

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

706 ) 

707 context["btn_show_tex"] = settings.SHOW_TEX if hasattr(settings, "SHOW_TEX") else False 

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

709 return context 

710 

711 

712class ItemViewClassFactory: 

713 views = { 

714 "article": ArticleView, 

715 "container": ContainerView, 

716 "collection": CollectionView, 

717 "volume": VolumeDetailView, 

718 } 

719 

720 def get_resource_view(self, classname): 

721 classname = classname.lower() 

722 if classname in self.views: 

723 return self.views[classname] 

724 return None 

725 

726 

727@require_http_methods(["GET"]) 

728def journals(request, api=False): 

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

730 

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

732 

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

734 

735 

736@require_http_methods(["GET"]) 

737def actas(request, api=False): 

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

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

740 

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

742 

743 

744@require_http_methods(["GET"]) 

745def books(request, api=False): 

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

747 

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

749 

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

751 

752 

753@require_http_methods(["GET"]) 

754def proceedings(request): 

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

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

757 

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

759 

760 

761class IssuesView(TemplateView): 

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

763 

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

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

766 

767 if journal and journal.coltype == "lecture-notes" and journal.pid != "CIF": 

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

769 return HttpResponseRedirect(url) 

770 

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

772 

773 def get_context_data(self, **kwargs): 

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

775 

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

777 

778 if journal is None: 

779 raise Http404 

780 try: 

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

782 if "numdam.org" in current_edition: 

783 current_edition = None 

784 except models.ExtLink.DoesNotExist: 

785 current_edition = None 

786 

787 result = model_helpers.get_volumes_in_collection(journal) 

788 volume_count = result["volume_count"] 

789 collections = [] 

790 for ancestor in journal.ancestors.all(): 

791 item = model_helpers.get_volumes_in_collection(ancestor) 

792 volume_count = max(0, volume_count) 

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

794 collections.append(item) 

795 

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

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

798 collections.append(result) 

799 if len(collections) > 1: 

800 collections.sort( 

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

802 reverse=True, 

803 ) 

804 

805 is_cr = False 

806 if ( 

807 hasattr(settings, "SITE_NAME") 

808 and len(settings.SITE_NAME) == 6 

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

810 ): 

811 is_cr = True 

812 

813 display_with_titles = True 

814 i = 0 

815 while display_with_titles and i < len(collections): 

816 col = collections[i] 

817 if len(col["sorted_issues"]) != 1: 

818 display_with_titles = False 

819 else: 

820 j = 0 

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

822 while display_with_titles and j < len(volumes): 

823 vol = volumes[j] 

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

825 if display_with_titles: 

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

827 display_with_titles = issue.title_tex != "" 

828 j += 1 

829 i += 1 

830 

831 context.update( 

832 { 

833 "obj": journal, 

834 "journal": journal, 

835 "sorted_issues": result["sorted_issues"], 

836 "coltype": journal.coltype, 

837 "volume_count": volume_count, 

838 "max_width": result["max_width"], 

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

840 "show_issues": True, 

841 "is_cr": is_cr, 

842 "current_edition": current_edition, 

843 "collections": collections, 

844 "display_with_titles": display_with_titles, 

845 } 

846 ) 

847 update_context_with_desc(context) 

848 

849 return context 

850 

851 

852@require_http_methods(["GET"]) 

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

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

855 if collection is not None: 

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

857 

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

859 

860 request.GET = queryDict 

861 request.path = path 

862 request.path_info = path 

863 

864 filters = [] 

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

866 

867 path = request.path 

868 search_path = path + "?" 

869 

870 try: 

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

872 except ValueError: 

873 page = 1 

874 

875 params = { 

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

877 "sorted_by": sorted_by, 

878 "order_by": order_by, 

879 "page": page, 

880 "filters": filters, 

881 "path": path, 

882 "search_path": search_path, 

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

884 "query": query, 

885 } 

886 

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

888 

889 

890def generic_books(request, coltype, params): 

891 rows = 20 

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

893 

894 params["rows"] = 20 

895 params["start"] = start 

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

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

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

899 else: 

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

901 

902 cmd = solr_cmds.solrInternalSearchCmd(params) 

903 results = cmd.do() 

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

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

906 

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

908 

909 if results is not None: 

910 if results.facets["collection_title_facets"]: 

911 has_one_collection_filter = False 

912 collection_title_filter = "" 

913 if "filters" in params: 

914 for filter_ in params["filters"]: 

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

916 has_one_collection_filter = ( 

917 True if not has_one_collection_filter else False 

918 ) 

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

920 

921 if has_one_collection_filter: 

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

923 if obj: 

924 book = obj.cast() 

925 container = book.get_container() 

926 collection = container.get_collection() 

927 if collection.title_tex == collection_title_filter: 

928 context["collection"] = collection 

929 else: 

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

931 if other_collection.title_tex == collection_title_filter: 

932 context["collection"] = other_collection 

933 

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

935 

936 context["query"] = params["query"] 

937 if settings.SITE_NAME == "numdam": 

938 context["numdam"] = True 

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

940 

941 

942@require_http_methods(["GET"]) 

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

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

945 

946 request.GET = queryDict 

947 request.path = path 

948 request.path_info = path 

949 

950 filters = [] 

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

952 

953 path = request.path 

954 search_path = path + "?" 

955 

956 try: 

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

958 except ValueError: 

959 page = 1 

960 

961 params = { 

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

963 "sorted_by": sorted_by, 

964 "order_by": order_by, 

965 "page": page, 

966 "filters": filters, 

967 "path": path, 

968 "search_path": search_path, 

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

970 "query": query, 

971 } 

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

973 

974 

975@require_http_methods(["GET"]) 

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

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

978 

979 request.GET = queryDict 

980 request.path = path 

981 request.path_info = path 

982 

983 filters = [] 

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

985 

986 collection = model_helpers.get_collection("CJPS") 

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

988 sorted_by = "number" 

989 order_by = "desc" 

990 else: 

991 found = False 

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

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

994 pid = lectures.pop() 

995 collection = model_helpers.get_collection(pid) 

996 if ( 

997 collection is not None 

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

999 ): 

1000 found = True 

1001 sorted_by = "pid" 

1002 order_by = "desc" 

1003 

1004 path = request.path 

1005 search_path = path + "?" 

1006 

1007 try: 

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

1009 except ValueError: 

1010 page = 1 

1011 

1012 params = { 

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

1014 "sorted_by": sorted_by, 

1015 "order_by": order_by, 

1016 "page": page, 

1017 "filters": filters, 

1018 "path": path, 

1019 "search_path": search_path, 

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

1021 "query": query, 

1022 } 

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

1024 

1025 

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

1027class SearchView(TemplateView): 

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

1029 GET = None 

1030 path = None 

1031 

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

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

1034 if query == "": 

1035 return HttpResponseRedirect(reverse("help")) 

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

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

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

1039 self.GET = queryDict 

1040 self.path = path 

1041 try: 

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

1043 except SuspiciousOperation: 

1044 result = HttpResponseBadRequest("Bad request") 

1045 

1046 return result 

1047 

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

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

1050 return HttpResponseRedirect(reverse("help")) 

1051 

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

1053 show_eprint = True if show_eprint == "on" else show_eprint 

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

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

1056 

1057 return HttpResponseRedirect(path) 

1058 

1059 def get_context_data(self, **kwargs): 

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

1061 

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

1063 eprint = False 

1064 

1065 try: 

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

1067 if "eprint" in parse: 

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

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

1070 except: 

1071 value = cache.get("eprint") 

1072 eprint = False if not value else value 

1073 finally: 

1074 cache.close() 

1075 

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

1077 

1078 if eprint is False: 

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

1080 

1081 search_path = self.path + "?" 

1082 qs = [] 

1083 

1084 keep_qs_in_display = True 

1085 i = 0 

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

1087 

1088 while qti: 

1089 if i > 0: 

1090 search_path += "&" 

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

1092 

1093 if qti == "date": 

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

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

1096 

1097 if qfi or qli: 

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

1099 

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

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

1102 else: 

1103 if qti == "author_ref": 

1104 keep_qs_in_display = False 

1105 

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

1107 

1108 if qi or i == 0: 

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

1110 if noti == "on": 

1111 noti = True 

1112 else: 

1113 noti = False 

1114 

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

1116 

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

1118 

1119 if noti: 

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

1121 

1122 i += 1 

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

1124 

1125 try: 

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

1127 except ValueError: 

1128 page = 1 

1129 

1130 if len(qs) < 1: 

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

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

1133 raise SuspiciousOperation("Bad request") 

1134 

1135 rows = 20 

1136 start = (page - 1) * rows 

1137 

1138 params = { 

1139 "filters": filters, 

1140 "qs": qs, 

1141 "page": page, 

1142 "start": start, 

1143 "rows": rows, 

1144 "search_path": search_path, 

1145 "path": self.path, 

1146 } 

1147 

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

1149 if sort: 

1150 params["sort"] = sort 

1151 

1152 cmd = solr_cmds.solrSearchCmd(params) 

1153 results = cmd.do() 

1154 

1155 if not keep_qs_in_display: 

1156 qs = [] 

1157 

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

1159 

1160 if results is not None: 

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

1162 

1163 context["journal"] = model_helpers.get_collection(settings.COLLECTION_PID) 

1164 

1165 return context 

1166 

1167 

1168@require_http_methods(["GET"]) 

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

1170 """ 

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

1172 @param api: 

1173 @return: 

1174 """ 

1175 

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

1177 

1178 request.GET = queryDict 

1179 request.path = path 

1180 request.path_info = path 

1181 

1182 # to set default value on letter 

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

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

1185 for filter_ in filters: 

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

1187 letter = filter_[39] 

1188 

1189 try: 

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

1191 except ValueError: 

1192 page = 1 

1193 

1194 rows = 60 

1195 

1196 all_authors = model_helpers.get_authors_by_letter(letter) 

1197 paginator = Paginator(all_authors, rows) 

1198 try: 

1199 authors = paginator.page(page) 

1200 except EmptyPage: 

1201 raise Http404 

1202 

1203 context = { 

1204 "authors": authors, 

1205 "letters": string.ascii_uppercase, 

1206 "letter_active": letter, 

1207 "query": query, 

1208 } 

1209 

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

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

1212 

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

1214 

1215 

1216@require_http_methods(["GET"]) 

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

1218 resource = model_helpers.get_resource(aid) 

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

1220 raise Http404 

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

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

1223 item = resource.cast() 

1224 citing = item.citations() 

1225 context = {"resource": item} 

1226 context["citing"] = citing 

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

1228 

1229 

1230@require_http_methods(["GET"]) 

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

1232 """ 

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

1234 si l'item n'existe pas : 404 

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

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

1237 """ 

1238 

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

1240 

1241 

1242@require_http_methods(["GET"]) 

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

1244 """ 

1245 return tex file of a Resource if embargo not present 

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

1247 If there is an embargo redirect to the resource page 

1248 """ 

1249 

1250 if len(relative_path) > 0: 

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

1252 

1253 type_extension = { 

1254 "pdf": "application/pdf", 

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

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

1257 "png": "image/png", 

1258 "jpg": "image/jpeg", 

1259 "html": "text/html", 

1260 } 

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

1262 

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

1264 # the article/issue binary file 

1265 if not binary_file_type: 

1266 binary_file_type = "self" 

1267 

1268 if "/" in pid: 

1269 resource = model_helpers.get_resource_by_doi(pid) 

1270 else: 

1271 resource = model_helpers.get_resource(pid) 

1272 

1273 if resource is not None: 

1274 resource = resource.cast() 

1275 

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

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

1278 

1279 

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

1281 """ 

1282 get the filename from the database 

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

1284 """ 

1285 if resource is None: 

1286 return None, 404 

1287 

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

1289 if not allow_local_pdf: 

1290 return None, 404 

1291 

1292 filename = None 

1293 status = 200 

1294 

1295 try: 

1296 # May return None if there is an embargo 

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

1298 except exceptions.ResourceDoesNotExist: 

1299 status = 404 

1300 

1301 # File is protected 

1302 if filename is None: 

1303 status = 403 

1304 

1305 return filename, status 

1306 

1307 

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

1309 if status == 404: 

1310 raise Http404 

1311 elif status == 403: 

1312 template_name = "403withRedirect.html" 

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

1314 else: 

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

1316 

1317 try: 

1318 file_ = open(full_filename, "rb") 

1319 except OSError: 

1320 print("cannot open ", full_filename) 

1321 raise Http404 

1322 else: 

1323 response = sendfile(request, full_filename) 

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

1325 file_.close() 

1326 return response 

1327 

1328 

1329########################################################################## 

1330# 

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

1332# 

1333########################################################################## 

1334class APILookupView(View): 

1335 def get(self, request): 

1336 title = request.GET["title"] 

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

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

1339 params = { 

1340 "qs": [ 

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

1342 ], 

1343 } 

1344 if year: 

1345 params["qs"].append( 

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

1347 ) 

1348 if authors: 

1349 params["qs"].append( 

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

1351 ) 

1352 cmd = solr_cmds.solrSearchCmd(params) 

1353 results = cmd.do() 

1354 if len(results.docs): 

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

1356 else: 

1357 data = {} 

1358 return JsonResponse(data) 

1359 

1360 

1361class APIFetchView(View): 

1362 def get(self, request): 

1363 data = "" 

1364 pid = request.GET["pid"] 

1365 if pid: 

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

1367 data = article.get_citation(with_formatting=True) 

1368 return HttpResponse(data) 

1369 

1370 

1371class ArticlesAPIView(View): 

1372 def get(self, request): 

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

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

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

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

1377 return JsonResponse( 

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

1379 ) 

1380 

1381 

1382class ArticleDumpAPIView(View): 

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

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

1385 

1386 if "/" in pid: 

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

1388 else: 

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

1390 if not article: 

1391 raise Http404 

1392 

1393 date_accepted = date_published = date_online_first = None 

1394 if article.date_accepted: 

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

1396 if article.date_online_first: 

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

1398 if article.date_published: 

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

1400 date_received = ( 

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

1402 ) 

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

1404 

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

1406 if author_names: 

1407 authors = "; ".join(author_names) 

1408 else: 

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

1410 if author_names: 

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

1412 else: 

1413 authors = None 

1414 

1415 page_count = article.get_article_page_count() 

1416 

1417 result = { 

1418 "title_tex": article.title_tex, 

1419 "title_html": article.title_html, 

1420 "trans_title_tex": article.trans_title_tex, 

1421 "trans_title_html": article.trans_title_html, 

1422 "lang": article.lang, 

1423 "doi": article.doi or None, 

1424 "pid": article.pid, 

1425 "authors": authors, 

1426 "issue_pid": article.my_container.pid, 

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

1428 "volume": article.my_container.volume, 

1429 "number": article.my_container.number, 

1430 "year": article.my_container.year, 

1431 "issue_title": article.my_container.title_tex, 

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

1433 "date_accepted": date_accepted, 

1434 "date_online_first": date_online_first, 

1435 "date_published": date_published, 

1436 "date_received": date_received, 

1437 "date_revised": date_revised, 

1438 "page_count": page_count, 

1439 "body_html": article.body_html, 

1440 "pci_section": article.get_pci_section(), 

1441 } 

1442 

1443 extids = [] 

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

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

1446 result["extids"] = extids 

1447 

1448 result["kwds"] = [ 

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

1450 for kwd in article.kwd_set.all() 

1451 ] 

1452 

1453 awards = [] 

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

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

1456 result["awards"] = awards 

1457 

1458 abstracts = [] 

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

1460 abstracts.append( 

1461 { 

1462 "lang": abstract.lang, 

1463 "value_tex": abstract.value_tex, 

1464 "value_html": abstract.value_html, 

1465 } 

1466 ) 

1467 result["abstracts"] = abstracts 

1468 

1469 bibitems = [] 

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

1471 bibitems.append(bib.citation_html) 

1472 result["bibitems"] = bibitems 

1473 

1474 return JsonResponse(result) 

1475 

1476 

1477class BookDumpAPIView(View): 

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

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

1480 

1481 book = model_helpers.get_container(pid) 

1482 if not book: 

1483 raise Http404 

1484 

1485 result = { 

1486 "title_tex": book.title_tex, 

1487 "title_html": book.title_html, 

1488 "doi": book.doi or "", 

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

1490 } 

1491 

1492 extids = [] 

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

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

1495 result["extids"] = extids 

1496 

1497 result["kwds"] = [ 

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

1499 ] 

1500 

1501 abstracts = [] 

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

1503 abstracts.append( 

1504 { 

1505 "lang": abstract.lang, 

1506 "value_tex": abstract.value_tex, 

1507 "value_html": abstract.value_html, 

1508 } 

1509 ) 

1510 result["abstracts"] = abstracts 

1511 

1512 bibitems = [] 

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

1514 bibitems.append(bib.citation_html) 

1515 result["bibitems"] = bibitems 

1516 

1517 return JsonResponse(result) 

1518 

1519 

1520class AllIssuesAPIView(View): 

1521 def get(self, request): 

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

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

1524 

1525 

1526class CollectionIssnAPIView(View): 

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

1528 collection = get_object_or_404( 

1529 models.Collection, 

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

1531 ) 

1532 if collection.parent: 

1533 url = ( 

1534 "" 

1535 if collection.parent.pid == collection.pid 

1536 else collection.parent.get_absolute_url() 

1537 ) 

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

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

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

1541 

1542 

1543class CollectionsAPIView(View): 

1544 def get(self, request): 

1545 collections_pids = list( 

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

1547 ) 

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

1549 

1550 

1551class CollectionExportCSV(View): 

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

1553 colid = kwargs.get("colid") 

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

1555 

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

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

1558 

1559 csv_header = [ 

1560 "doi", 

1561 "title", 

1562 "date_accepted", 

1563 "date_first_publication", 

1564 ] 

1565 

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

1567 writer.writerow(csv_header) 

1568 

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

1570 my_container__my_collection__pid=colid 

1571 ).order_by("-date_accepted"): 

1572 if article.date_published is not None or article.date_online_first is not None: 

1573 first_online = ( 

1574 article.date_online_first 

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

1576 else article.date_published 

1577 ) 

1578 writer.writerow( 

1579 [ 

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

1581 xml_utils.normalise_span(article.title_tex), 

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

1583 if article.date_accepted is not None 

1584 else "", 

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

1586 ] 

1587 ) 

1588 

1589 return response 

1590 

1591 

1592class IssuesAPIView(View): 

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

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

1595 issues_pids = list( 

1596 models.Container.objects.filter( 

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

1598 ) 

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

1600 .order_by("pid") 

1601 ) 

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

1603 

1604 

1605class IssueListAPIView(View): 

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

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

1608 articles_pids = list( 

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

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

1611 .order_by("pid") 

1612 ) 

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

1614 

1615 

1616class ItemXMLView(View): 

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

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

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

1620 

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

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

1623 # The id given is a DOI 

1624 article = model_helpers.get_article_by_doi(pid) 

1625 if article: 

1626 pid = article.pid 

1627 else: 

1628 raise Http404 

1629 

1630 xml_body = ptf_cmds.exportPtfCmd( 

1631 { 

1632 "pid": pid, 

1633 "with_internal_data": False, 

1634 "with_binary_files": False, 

1635 "for_archive": True, 

1636 "full_xml": full_xml, 

1637 } 

1638 ).do() 

1639 

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

1641 

1642 

1643class ItemFileListAPIView(View): 

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

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

1646 

1647 resource = model_helpers.get_resource(pid) 

1648 if not resource: 

1649 raise Http404 

1650 

1651 obj = resource.cast() 

1652 binary_files = obj.get_binary_files_location() 

1653 

1654 result = {"files": binary_files} 

1655 

1656 if obj.classname == "Container": 

1657 articles = [] 

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

1659 article_files = article.get_binary_files_location() 

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

1661 result["articles"] = articles 

1662 

1663 return JsonResponse(result) 

1664 

1665 

1666class APIMatchOneView(View): 

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

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

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

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

1671 

1672 resource = model_helpers.get_resource(pid) 

1673 if not resource: 

1674 raise Http404 

1675 

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

1677 return HttpResponseBadRequest( 

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

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

1680 ) 

1681 result = "" 

1682 

1683 status = 200 

1684 message = "" 

1685 

1686 try: 

1687 obj = resource.cast() 

1688 seq = int(seq) 

1689 if seq == 0: 

1690 # Article, Book or Book part match 

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

1692 result = matching.match_article(obj, what) 

1693 else: 

1694 # TODO match book 

1695 pass 

1696 else: 

1697 bibitem = model_helpers.get_bibitem_by_seq(obj, seq) 

1698 if not bibitem: 

1699 raise Http404 

1700 result = matching.match_bibitem(bibitem, what) 

1701 

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

1703 except Timeout as exception: 

1704 return HttpResponse(exception, status=408) 

1705 except Exception as exception: 

1706 return HttpResponseServerError(exception) 

1707 

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

1709 return JsonResponse(data) 

1710 

1711 

1712class APIMatchAllView(View): 

1713 @staticmethod 

1714 def get_existing_ids(pid, what, force): 

1715 query_bibitemids = ( 

1716 models.BibItemId.objects.filter( 

1717 bibitem__resource__pid__contains=pid, 

1718 id_type__in=what, 

1719 ) 

1720 .select_related( 

1721 "bibitem", 

1722 "bibitem__resource", 

1723 ) 

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

1725 ) 

1726 query_extids = models.ExtId.objects.filter( 

1727 resource__pid__contains=pid, 

1728 id_type__in=what, 

1729 ).select_related("resource") 

1730 if force == "1": 

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

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

1733 if force == "2": 

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

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

1736 bibitemids = { 

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

1738 for bibitemid in query_bibitemids 

1739 } 

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

1741 return bibitemids.union(extids) 

1742 

1743 @staticmethod 

1744 def get_possible_ids(pid, what): 

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

1746 "resource" 

1747 ) 

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

1749 classname="TranslatedArticle" 

1750 ) 

1751 bibitemids = { 

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

1753 for type_ in what 

1754 for item in bibitems 

1755 if type_ != "pmid" 

1756 } 

1757 # we remove doi from possible extids 

1758 if "doi" in what: 

1759 what.remove("doi") 

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

1761 return bibitemids.union(extids) 

1762 

1763 @staticmethod 

1764 def delete_ids_if_necessary(pid, what, force): 

1765 if force == "1": 

1766 models.BibItemId.objects.filter( 

1767 bibitem__resource__pid__contains=pid, 

1768 id_type__in=what, 

1769 checked=False, 

1770 false_positive=False, 

1771 ).delete() 

1772 models.ExtId.objects.filter( 

1773 resource__pid__contains=pid, 

1774 id_type__in=what, 

1775 checked=False, 

1776 false_positive=False, 

1777 ).delete() 

1778 if force == "2": 

1779 models.BibItemId.objects.filter( 

1780 bibitem__resource__pid__contains=pid, id_type__in=what 

1781 ).delete() 

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

1783 

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

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

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

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

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

1789 what = what.split("_") 

1790 if force != "0": 

1791 self.delete_ids_if_necessary(pid, what, force) 

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

1793 possible = self.get_possible_ids(pid, what) 

1794 ids = list(possible - existing) 

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

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

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

1798 

1799 

1800class UpdateMatchingView(View): 

1801 resource = None 

1802 

1803 def post_update(self): 

1804 model_helpers.post_resource_updated(self.resource) 

1805 

1806 def update_obj(self, action): 

1807 if action == "delete": 

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

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

1810 elif action == "mark-checked": 

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

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

1813 elif action == "mark-unchecked": 

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

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

1816 

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

1818 cmd = xml_cmds.updateBibitemCitationXmlCmd() 

1819 cmd.set_bibitem(bibitem) 

1820 cmd.do() 

1821 

1822 self.post_update() 

1823 

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

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

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

1827 

1828 self.resource = model_helpers.get_resource(pid) 

1829 if not self.resource: 

1830 raise Http404 

1831 

1832 self.update_obj(action) 

1833 

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

1835 return HttpResponseRedirect(url) 

1836 

1837 

1838class UpdateExtIdView(View): 

1839 obj = None 

1840 resource = None 

1841 parent = None 

1842 

1843 def get_obj(self, pk): 

1844 try: 

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

1846 except models.ExtId.DoesNotExist: 

1847 raise Http404 

1848 

1849 self.obj = extid 

1850 self.resource = extid.resource 

1851 

1852 def post_update(self): 

1853 model_helpers.post_resource_updated(self.resource) 

1854 

1855 def update_obj(self, action): 

1856 if not self.obj: 

1857 raise Http404 

1858 

1859 if action == "delete": 

1860 self.obj.delete() 

1861 elif action == "toggle-checked": 

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

1863 self.obj.save() 

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

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

1866 self.obj.save() 

1867 

1868 self.post_update() 

1869 

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

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

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

1873 

1874 self.get_obj(pk) 

1875 self.update_obj(action) 

1876 

1877 if action == "delete": 

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

1879 return HttpResponseRedirect(url) 

1880 return JsonResponse({}) 

1881 

1882 

1883class UpdateBibItemIdView(UpdateExtIdView): 

1884 def get_obj(self, pk): 

1885 try: 

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

1887 except models.BibItemId.DoesNotExist: 

1888 raise Http404 

1889 

1890 self.obj = bibitemid 

1891 self.parent = bibitemid.bibitem 

1892 self.resource = bibitemid.bibitem.resource 

1893 

1894 def post_update(self): 

1895 cmd = xml_cmds.updateBibitemCitationXmlCmd() 

1896 cmd.set_bibitem(self.parent) 

1897 cmd.do() 

1898 

1899 model_helpers.post_resource_updated(self.resource) 

1900 

1901 

1902class APIFetchId(View): 

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

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

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

1906 pk = kwargs.get("pk") 

1907 resource = kwargs["resource"] 

1908 

1909 if what == "pmid": 

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

1911 return JsonResponse(data) 

1912 

1913 if not id_: 

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

1915 

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

1917 return HttpResponseBadRequest( 

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

1919 ) 

1920 

1921 try: 

1922 result = matching.fetch_id(id_, what) 

1923 except (IndexError, RequestException) as exception: 

1924 return HttpResponseServerError(exception) 

1925 

1926 if resource == "bibitemid": 

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

1928 raw = bibitem.citation_html 

1929 else: 

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

1931 raw = article.get_citation(request) 

1932 result = highlight_diff(raw, result) 

1933 

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

1935 return JsonResponse(data) 

1936 

1937 

1938class APIFetchAllView(View): 

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

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

1941 

1942 article = model_helpers.get_article(pid) 

1943 if not article: 

1944 raise Http404 

1945 

1946 ids = matching.get_all_fetch_ids(article) 

1947 

1948 data = {"ids": ids} 

1949 return JsonResponse(data) 

1950 

1951 

1952@require_http_methods(["GET"]) 

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

1954 template = "malsm.html" 

1955 context = { 

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

1957 "coltype": "book", 

1958 "template": template, 

1959 } 

1960 

1961 return render(request, template, context) 

1962 

1963 

1964class LatestArticlesFeed(Feed): 

1965 link = "" 

1966 ttl = 120 

1967 

1968 def get_feed(self, obj, request): 

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

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

1971 return feed 

1972 

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

1974 """ 

1975 Select the site whose RSS feed is requested. 

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

1977 """ 

1978 self.request = request 

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

1980 

1981 def title(self, obj): 

1982 if obj.requested_col_id: 

1983 collection = get_object_or_404( 

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

1985 ) 

1986 else: 

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

1988 return collection.title_sort 

1989 

1990 def description(self): 

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

1992 

1993 def items(self, obj): 

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

1995 if obj.requested_col_id: 

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

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

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

1999 )[:20] 

2000 

2001 def item_title(self, item): 

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

2003 

2004 def item_description(self, item): 

2005 language = get_language() 

2006 abstracts = item.get_abstracts() 

2007 if not abstracts: 

2008 return self.item_title(item) 

2009 try: 

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

2011 except models.Abstract.DoesNotExist: 

2012 return abstracts.first().value_html 

2013 

2014 def item_pubdate(self, item): 

2015 if item.date_published: 

2016 return item.date_published 

2017 return item.date_online_first 

2018 

2019 def item_link(self, item): 

2020 if item.doi: 

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

2022 return item.get_absolute_url() 

2023 

2024 

2025class LatestIssue(ItemView): 

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

2027 pid = kwargs.get("pid") 

2028 if pid is None: 

2029 return HttpResponse(status=404) 

2030 

2031 container_issues = ( 

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

2033 .all() 

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

2035 ) 

2036 if container_issues is None: 

2037 return HttpResponse(status=404) 

2038 

2039 container = container_issues.first() 

2040 url = container.get_absolute_url() 

2041 return HttpResponseRedirect(url) 

2042 

2043 

2044class RSSTemplate(TemplateView): 

2045 template_name = "blocks/syndication_feed.html" 

2046 

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

2048 # Numdam does not have an rss feed for now 

2049 if settings.SITE_ID == 3: 

2050 raise Http404 

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

2052 

2053 def get_context_data(self, **kwargs): 

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

2055 

2056 context["journal"] = model_helpers.get_collection(settings.COLLECTION_PID) 

2057 

2058 return context 

2059 

2060 

2061class ArticleEditAPIView(View): 

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

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

2064 self.doi = None 

2065 self.colid = None 

2066 self.edit_all_fields = False 

2067 self.fields_to_update = ( 

2068 [] 

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

2070 

2071 @method_decorator(csrf_exempt) 

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

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

2074 

2075 def save_data(self, data_article): 

2076 pass 

2077 

2078 def restore_data(self, article): 

2079 pass 

2080 

2081 def convert_data_for_editor(self, data_article): 

2082 data_article.trans_title_formulas = [] 

2083 data_article.title_formulas = [] 

2084 data_article.abstract_formulas = [] 

2085 data_article.trans_abstract_formulas = [] 

2086 

2087 data_article.title_tex, data_article.trans_title_tex = get_tex_from_xml( 

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

2089 ) 

2090 

2091 for contrib in data_article.contributors: 

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

2093 

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

2095 data_article.abstracts = [ 

2096 { 

2097 "tag": "abstract", 

2098 "lang": data_article.lang, 

2099 "value_html": "", 

2100 "value_tex": "", 

2101 "value_xml": "", 

2102 } 

2103 ] 

2104 

2105 for abstract in data_article.abstracts: 

2106 if abstract["value_xml"]: 

2107 value_tex = get_tex_from_xml( 

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

2109 ) 

2110 else: 

2111 value_tex = "" 

2112 abstract["value_tex"] = value_tex 

2113 

2114 data_article.conference = ", ".join( 

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

2116 ) 

2117 data_article.topics = [ 

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

2119 ] 

2120 

2121 data_article.pci_section = "".join( 

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

2123 ) 

2124 

2125 data_article.subjs = [ 

2126 subj 

2127 for subj in data_article.subjs 

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

2129 ] 

2130 

2131 with_ordered_label = True 

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

2133 model_data_converter.convert_refdata_for_editor(ref) 

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

2135 with_ordered_label = False 

2136 data_article.bibitems_with_ordered_label = with_ordered_label 

2137 

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

2139 data_article.icon_url = None 

2140 if ext_link: 

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

2142 

2143 def convert_data_from_editor(self, data_article): 

2144 xtitle = CkeditorParser( 

2145 html_value=data_article.title_tex, 

2146 mml_formulas=data_article.title_formulas, 

2147 ignore_p=True, 

2148 ) 

2149 data_article.title_html = xtitle.value_html 

2150 data_article.title_tex = xtitle.value_tex 

2151 

2152 trans_title_xml = "" 

2153 if data_article.trans_title_tex: 

2154 xtranstitle = CkeditorParser( 

2155 html_value=data_article.trans_title_tex, 

2156 mml_formulas=data_article.trans_title_formulas, 

2157 ignore_p=True, 

2158 ) 

2159 data_article.trans_title_html = xtranstitle.value_html 

2160 data_article.trans_title_tex = xtranstitle.value_tex 

2161 trans_title_xml = xtranstitle.value_xml 

2162 

2163 data_article.title_xml = get_title_xml( 

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

2165 ) 

2166 

2167 for contrib in data_article.contributors: 

2168 contrib["addresses"] = [] 

2169 if "address_text" in contrib: 

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

2171 

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

2173 if i > 0: 

2174 xabstract = CkeditorParser( 

2175 html_value=abstract["value_tex"], 

2176 mml_formulas=data_article.trans_abstract_formulas, 

2177 ) 

2178 else: 

2179 xabstract = CkeditorParser( 

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

2181 ) 

2182 abstract["value_html"] = xabstract.value_html 

2183 abstract["value_tex"] = xabstract.value_tex 

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

2185 abstract[ 

2186 "value_xml" 

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

2188 else: 

2189 lang = abstract["lang"] 

2190 abstract[ 

2191 "value_xml" 

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

2193 

2194 data_article.subjs = [ 

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

2196 ] 

2197 if data_article.topics: 

2198 data_article.subjs.extend( 

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

2200 ) 

2201 if data_article.conference: 

2202 data_article.subjs.append( 

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

2204 ) 

2205 if data_article.pci_section: 

2206 data_article.subjs.append( 

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

2208 ) 

2209 

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

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

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

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

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

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

2216 ref["doi"] = doi 

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

2218 else: 

2219 ref["extids"] = [] 

2220 # URLs are in <comment> 

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

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

2223 # 'mimetype': '', 

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

2225 # 'base': '', 

2226 # 'metadata': ''}] 

2227 # else: 

2228 # ref['ext_links'] = [] 

2229 

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

2231 contribs = [] 

2232 for author_txt in author_array: 

2233 if author_txt: 

2234 lastname = firstname = "" 

2235 pos = author_txt.find(", ") 

2236 if pos > 0: 

2237 lastname = author_txt[0:pos] 

2238 firstname = author_txt[pos + 2 :] 

2239 else: 

2240 lastname = author_txt 

2241 

2242 contrib = model_data.create_contributor() 

2243 contrib["first_name"] = firstname 

2244 contrib["last_name"] = lastname 

2245 contrib["role"] = "author" 

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

2247 contribs.append(contrib) 

2248 ref["contributors"] = contribs 

2249 

2250 def replace_p(self, obj, key): 

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

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

2253 if pos > -1: 

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

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

2256 obj[key] = text 

2257 

2258 def replace_return(self, obj, key): 

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

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

2261 obj[key] = text 

2262 

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

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

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

2266 

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

2268 if not article: 

2269 raise Http404 

2270 

2271 data_article = model_data_converter.db_to_article_data(article) 

2272 self.convert_data_for_editor(data_article) 

2273 

2274 def obj_to_dict(obj): 

2275 return obj.__dict__ 

2276 

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

2278 

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

2280 

2281 def update_article(self, article_data, icon_file): 

2282 self.save_data(article_data) 

2283 

2284 collection = model_helpers.get_collection(self.colid) 

2285 

2286 model_data_converter.update_data_for_jats(article_data) 

2287 

2288 issue = None 

2289 existing_article = model_helpers.get_article_by_doi(article_data.doi) 

2290 new_data_article = None 

2291 

2292 if existing_article is not None: 

2293 issue = existing_article.my_container 

2294 new_data_article = model_data_converter.db_to_article_data(existing_article) 

2295 else: 

2296 new_data_article = model_data.create_articledata() 

2297 new_data_article.pid = article_data.pid 

2298 new_data_article.doi = article_data.doi 

2299 

2300 if self.edit_all_fields: 

2301 new_data_article = article_data 

2302 else: 

2303 for field in self.fields_to_update: 

2304 value = getattr(article_data, field) 

2305 setattr(new_data_article, field, value) 

2306 

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

2308 # New icon 

2309 if icon_file and issue: 

2310 relative_file_name = resolver.copy_file_obj_to_article_folder( 

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

2312 ) 

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

2314 if ext_link is None: 

2315 ext_link = model_data.create_extlink() 

2316 ext_link["rel"] = "icon" 

2317 ext_link["location"] = relative_file_name 

2318 new_data_article.ext_links.append(ext_link) 

2319 

2320 # No or removed icon 

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

2322 new_data_article.ext_links = [ 

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

2324 ] 

2325 

2326 cmd = xml_cmds.addArticleXmlCmd( 

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

2328 ) 

2329 cmd.set_collection(collection) 

2330 article = cmd.do() 

2331 

2332 self.restore_data(article) 

2333 

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

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

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

2337 

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

2339 body = json.loads(body_unicode) 

2340 

2341 article_data = Munch(body) 

2342 new_bibitems = [] 

2343 for bib in article_data.bibitems: 

2344 new_bibitems.append(Munch(bib)) 

2345 article_data.bibitems = new_bibitems 

2346 

2347 new_relations = [] 

2348 for relation in article_data.relations: 

2349 new_relations.append(Munch(relation)) 

2350 article_data.relations = new_relations 

2351 

2352 new_translations = [] 

2353 for translation in article_data.translations: 

2354 new_translations.append(Munch(translation)) 

2355 article_data.translations = new_translations 

2356 

2357 self.convert_data_from_editor(article_data) 

2358 

2359 icon_file = None 

2360 if "icon" in request.FILES: 

2361 icon_file = request.FILES["icon"] 

2362 

2363 self.update_article(article_data, icon_file) 

2364 

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

2366 

2367 

2368class CollectionEditAPIView(View): 

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

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

2371 self.colid = None 

2372 self.collection = None 

2373 self.edit_all_fields = True 

2374 self.fields_to_update = ( 

2375 [] 

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

2377 

2378 @method_decorator(csrf_exempt) 

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

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

2381 

2382 def save_data(self, data): 

2383 pass 

2384 

2385 def restore_data(self, collection): 

2386 pass 

2387 

2388 def convert_data_for_editor(self, data): 

2389 data.title, data.trans_title = get_tex_from_xml( 

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

2391 ) 

2392 # TODO: move periods inside GDML 

2393 data.periods = [] 

2394 

2395 def convert_data_from_editor(self, data): 

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

2397 data.title_html = xtitle.value_html 

2398 data.title_tex = xtitle.value_tex 

2399 

2400 # TODO: get_title_xml for Collections 

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

2402 

2403 ids = [] 

2404 if data.issn: 

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

2406 if data.e_issn: 

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

2408 data.ids = ids 

2409 

2410 def replace_p(self, obj, key): 

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

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

2413 if pos > -1: 

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

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

2416 obj[key] = text 

2417 

2418 def replace_return(self, obj, key): 

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

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

2421 obj[key] = text 

2422 

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

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

2425 

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

2427 if not collection: 

2428 raise Http404 

2429 

2430 data = model_data_converter.db_to_publication_data(collection) 

2431 self.convert_data_for_editor(data) 

2432 

2433 def obj_to_dict(obj): 

2434 return obj.__dict__ 

2435 

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

2437 

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

2439 

2440 def update_collection(self, data): 

2441 self.save_data(data) 

2442 

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

2444 if existing_collection is None: 

2445 cls = ptf_cmds.addCollectionPtfCmd 

2446 new_data = data 

2447 else: 

2448 cls = ptf_cmds.updateCollectionPtfCmd 

2449 

2450 if self.edit_all_fields: 

2451 new_data = data 

2452 else: 

2453 new_data = model_data_converter.db_to_publication_data(existing_collection) 

2454 

2455 for field in self.fields_to_update: 

2456 value = getattr(data, field) 

2457 setattr(new_data, field, value) 

2458 

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

2460 cmd = cls(params) 

2461 if new_data.provider: 

2462 provider = model_helpers.get_provider_by_name(new_data.provider) 

2463 else: 

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

2465 cmd.set_provider(provider) 

2466 collection = cmd.do() 

2467 

2468 # TODO: Move xml_cmds add_objects_with_location inside ptf_cmds 

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

2470 

2471 self.restore_data(collection) 

2472 

2473 return collection 

2474 

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

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

2477 

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

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

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

2481 body = json.loads(body_unicode) 

2482 

2483 data = Munch(body) 

2484 self.convert_data_from_editor(data) 

2485 

2486 self.update_collection(data) 

2487 

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

2489 

2490 

2491class ArticleCitedByView(View): 

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

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

2494 

2495 if "/" in doi: 

2496 resource = model_helpers.get_resource_by_doi(doi) 

2497 else: 

2498 resource = model_helpers.get_resource(doi) 

2499 

2500 if not resource: 

2501 raise Http404 

2502 

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

2504 if citations: 

2505 status_code = 200 

2506 else: 

2507 status_code = 204 

2508 

2509 data = { 

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

2511 "status": status_code, 

2512 } 

2513 return JsonResponse(data) 

2514 

2515 

2516def export_citation(request, **kwargs): 

2517 data = "" 

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

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

2520 

2521 if "/" in pid: 

2522 resource = model_helpers.get_resource_by_doi(pid) 

2523 else: 

2524 resource = model_helpers.get_resource(pid) 

2525 

2526 if not resource: 

2527 return HttpResponse(status=404) 

2528 

2529 if hasattr(resource, "article"): 

2530 document = resource.article 

2531 else: 

2532 document = model_helpers.get_container(resource.pid) 

2533 

2534 filename = "citation." + ext 

2535 if ext == "bib": 

2536 data = document.get_bibtex(request) 

2537 elif ext == "ris": 

2538 data = document.get_ris(request) 

2539 elif ext == "enw": 

2540 data = document.get_endnote(request) 

2541 

2542 if data: 

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

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

2545 else: 

2546 response = HttpResponse(status=204) 

2547 return response 

2548 

2549 

2550def get_tokenized(body): 

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

2552 math_symbols = {} 

2553 i = 0 

2554 

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

2556 if tag.name == "span": 

2557 math_symbols[i] = tag.decode_contents() 

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

2559 i += 1 

2560 # else: 

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

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

2563 # j += 1 

2564 

2565 body = str(soup) 

2566 

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

2568 

2569 

2570def detokenize(body, tokens): 

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

2572 

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

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

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

2576 id = int(id_list[0]) 

2577 if id in tokens["math"]: 

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

2579 

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

2581 

2582 return body 

2583 

2584 

2585class ExportArticleHtml(View): 

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

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

2588 if doi[-1] == "/": 

2589 doi = doi[:-1] 

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

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

2592 body = a.body_html 

2593 pid = a.__str__() 

2594 

2595 if tokenize is not None: 

2596 # tokenize = int(tokenize) 

2597 

2598 tokens_infos = get_tokenized(body) 

2599 body = tokens_infos["body"] 

2600 # tokens = tokens_infos["tokens"] 

2601 

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

2603 html_article.write(body) 

2604 html_article.seek(0) 

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

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

2607 

2608 return response 

2609 

2610 

2611class RecentArticlesPublished(View): 

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

2613 # - titre de l'article 

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

2615 # - collection de l'article 

2616 # - doi 

2617 # - Infos sur l'article 

2618 # - url 

2619 

2620 def get(self, request): 

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

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

2623 )[:100] 

2624 articles_infos = [] 

2625 container_pids = [] 

2626 nb_articles = articles.count() 

2627 i = 0 

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

2629 article = articles[i] 

2630 if article.my_container is not None and article.my_container.pid not in container_pids: 

2631 container_pids.append(article.my_container.pid) 

2632 if article.date_published: 

2633 item = { 

2634 "title": article.title_html, 

2635 "authors": article.get_authors_short(), 

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

2637 "doi": article.doi, 

2638 "citation_source": article.get_citation_base(), 

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

2640 } 

2641 articles_infos.append(item) 

2642 i += 1 

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

2644 

2645 

2646def get_first_n_authors(authors, n=3): 

2647 if len(authors) > n: 

2648 authors = authors[0:n] 

2649 authors.append("...") 

2650 return "; ".join(authors) 

2651 

2652 

2653def get_suggested_articles(article): 

2654 documents = [] 

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

2656 solr_cmds.auto_suggest_doi(obj, article) 

2657 

2658 if obj.doi_list: 

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

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

2661 dois = [ 

2662 doi 

2663 for doi in obj.doi_list.split() 

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

2665 ] 

2666 for doi in dois: 

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

2668 doc = {} 

2669 base_url = "" 

2670 if suggest: 

2671 doc = vars(suggest) 

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

2673 if suggest.my_container: 

2674 collection = suggest.my_container.my_collection 

2675 base_url = collection.website() or "" 

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

2677 doc["journal_abbrev"] = collection.abbrev 

2678 else: 

2679 try: 

2680 doc = crossref.crossref_request(doi) 

2681 except: 

2682 continue 

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

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

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

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

2687 

2688 if doc: 

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

2690 if base_url and suggest: 

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

2692 else: 

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

2694 documents.append(doc) 

2695 return documents 

2696 

2697 

2698@csrf_exempt 

2699def update_suggest(request, doi): 

2700 if request.method == "POST": 

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

2702 if not article: 

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

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

2705 obj.resource_doi = article.doi 

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

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

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

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

2710 obj.save() 

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

2712 

2713 

2714@csrf_exempt 

2715def graphical_abstract(request, doi): 

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

2717 if request.method == "POST": 

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

2719 obj.resource_doi = article.doi 

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

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

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

2723 obj.save() 

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

2725 elif request.method == "DELETE": 

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

2727 if obj: 

2728 obj.delete() 

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

2730 

2731 

2732class MoreLikeThisView(View): 

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

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

2735 article = model_helpers.get_resource_by_doi(doi) 

2736 if not article: 

2737 raise Http404 

2738 

2739 results = solr_cmds.research_more_like_this(article) 

2740 return JsonResponse(results) 

2741 

2742 

2743class HelpView(TemplateView): 

2744 template_name = "help.html" 

2745 

2746 def get_context_data(self, **kwargs): 

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

2748 

2749 context["journal"] = model_helpers.get_collection(settings.COLLECTION_PID) 

2750 

2751 return context