Coverage for src/ptf/views/base_views.py: 49%
1588 statements
« prev ^ index » next coverage.py v7.6.4, created at 2024-11-05 09:56 +0000
« prev ^ index » next coverage.py v7.6.4, created at 2024-11-05 09:56 +0000
1import csv
2import html
3import json
4import os
5import re
6import string
7import urllib.parse
8from itertools import chain
9from operator import itemgetter
11from bs4 import BeautifulSoup
12from django_sendfile import sendfile
13from munch import Munch
14from requests.exceptions import RequestException
15from requests.exceptions import Timeout
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
46from ..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.builder.issue import get_title_xml
61from ptf.cmds.xml.jats.jats_parser import get_tex_from_xml
62from ptf.display import resolver
63from ptf.display import utils
64from ptf.model_data_converter import jats_from_abstract
65from ptf.models import Article
66from ptf.solr.search_helpers import CleanSearchURL
67from ptf.templatetags.helpers import decode_query_string
68from ptf.url_utils import format_url_with_params
69from ptf.utils import highlight_diff
70from ptf.views.components import breadcrumb
72# from django.utils.crypto import md5
73# from django.utils import cache
74#
75# def ptf_generate_cache_key(request, method, headerlist, key_prefix):
76# """Return a cache key from the headers given in the header list."""
77# url = md5(request.build_absolute_uri().encode("ascii"), usedforsecurity=False)
78# cache_key = "views.decorators.cache.cache_page.%s.%s.%s" % (
79# key_prefix,
80# method,
81# url.hexdigest(),
82# )
83# return cache_key
84#
85#
86# def ptf_generate_cache_header_key(key_prefix, request):
87# """Return a cache key for the header cache."""
88# url = md5(request.build_absolute_uri().encode("ascii"), usedforsecurity=False)
89# cache_key = "views.decorators.cache.cache_header.%s.%s" % (
90# key_prefix,
91# url.hexdigest(),
92# )
93# return cache_key
94#
95#
96# cache._generate_cache_key = ptf_generate_cache_key
97# cache._generate_cache_header_key = ptf_generate_cache_header_key
100def update_context_with_desc(context):
101 online_first_cr = context.get("online_first_cr", False)
102 context[
103 "online_first_desc_fr"
104 ] = """<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>"""
105 context[
106 "online_first_desc_fr"
107 ] += """<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>"""
108 if not online_first_cr:
109 context[
110 "online_first_desc_fr"
111 ] += """<p>Dès que les articles sont affectés à un numéro de la revue, ils sont retirés de cette page.</p>"""
113 context[
114 "online_first_desc_en"
115 ] = """<p>Online First articles have been peer-reviewed, accepted and edited. They have been formatted and finalized with corrections from authors and proofreaders.</p>"""
116 context[
117 "online_first_desc_en"
118 ] += """<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>"""
119 if not online_first_cr:
120 context[
121 "online_first_desc_en"
122 ] += """<p>When the articles are assigned to an issue, they will be removed from this page.</p>"""
125@require_http_methods(["POST"])
126def set_formula_display(request):
127 next = request.headers.get("referer")
128 # if formula-display is not in POST, then mathml is choosen
129 formula_display = request.POST.get("formula-display", "mathml")
130 request.session["formula_display"] = formula_display
131 # redis session needs to save item in state
132 request.session.save()
134 # to read session with redis-django-sessions
135 # rdata = request.session.load()
136 return HttpResponseRedirect(next)
139# def views_get_collection(request, collection, api=False, *args, **kwargs):
140# if not collection:
141# raise Http404
142#
143# if collection.coltype in ['journal', 'acta']:
144# return IssuesView.as_view()(request, jid=collection.pid)
145# elif collection.coltype == 'these':
146# return thesis(request)
147# else:
148# # en theorie: coltype=book-series TODO: check lectures
149# url = ('series/"%s"/' % collection.title_html) + "p"
150# path, queryDict = CleanSearchURL.decode(url)
151# request.GET = queryDict
152# request.path = path
153# request.path_info = path
154# return sorted_books(request)
157# @require_http_methods(["GET"])
158# def container(request, pid, api=False):
159# cont = model_helpers.get_container(pid)
160# return views_get_container(request, cont, api)
163def views_update_container_context(request, container, context, display_lang):
164 if container is None: 164 ↛ 165line 164 didn't jump to line 165 because the condition on line 164 was never true
165 raise Http404
167 articles_to_appear = is_cr = False
168 if hasattr(settings, "ISSUE_TO_APPEAR_PID") and settings.ISSUE_TO_APPEAR_PID == container.pid: 168 ↛ 169line 168 didn't jump to line 169 because the condition on line 168 was never true
169 context["articles_to_appear"] = articles_to_appear = True
171 if (
172 hasattr(settings, "SITE_NAME")
173 and len(settings.SITE_NAME) == 6
174 and settings.SITE_NAME[0:2] == "cr"
175 ):
176 context["is_cr"] = is_cr = True
178 context["online_first_cr"] = False
179 if container.with_online_first and is_cr:
180 context["articles_to_appear"] = articles_to_appear = True
181 context["online_first_cr"] = True
183 collection = container.my_collection
184 context["citing_articles"] = container.citations()
185 context["source"] = request.GET.get("source", None)
187 context["breadcrumb"] = breadcrumb.get_breadcrumb(container)
188 context["btn_show_tex"] = settings.SHOW_TEX if hasattr(settings, "SHOW_TEX") else False
190 if container.ctype.startswith("book") or container.ctype == "lecture-notes":
191 book_parts = container.article_set.filter(sites__id=settings.SITE_ID).all().order_by("seq")
193 references = False
194 if container.ctype == "book-monograph":
195 # on regarde si il y a au moins une bibliographie
196 for art in container.article_set.all(): 196 ↛ 197line 196 didn't jump to line 197 because the loop on line 196 never started
197 if art.bibitem_set.count() > 0:
198 references = True
199 context["references"] = references
201 context["book"] = container
202 context["book_parts"] = list(book_parts)
203 context["template"] = "blocks/book.html"
205 else:
206 if articles_to_appear and is_cr:
207 articles = container.article_set.all().order_by(
208 F("date_online_first").desc(nulls_last=True), "-seq"
209 )
211 else:
212 articles = container.article_set.all().order_by("seq")
213 # TODO next_issue, previous_issue
215 context["issue"] = container
216 context["articles"] = articles
217 context["template"] = "blocks/issue-items.html"
219 # commun article ?
220 context["coltype"] = collection.coltype
221 context["supplementary_materials"] = models.SupplementaryMaterial.objects.filter(
222 relatedobject_ptr__resource__pk=container.pk,
223 ).filter(rel="supplementary-material")
224 context["reviews"] = models.SupplementaryMaterial.objects.filter(
225 relatedobject_ptr__resource__pk=container.pk,
226 ).filter(rel="review")
227 context["bibtex"] = container.get_bibtex(request)
228 context["ris"] = container.get_ris(request)
229 context["enw"] = container.get_endnote(request)
230 context["howtocite"] = container.get_citation(request)
231 # context['bibtex'] = article.get_bibtex(request.get_host()) pas la meme signature que pour article ?
232 # context['howtocite'] = article.get_citation(request)
234 if collection.pid == "ART": 234 ↛ 235line 234 didn't jump to line 235 because the condition on line 234 was never true
235 qs = collection.content.filter(sites__id=settings.SITE_ID).order_by(
236 "-vseries_int", "-year", "-volume_int", "number_int"
237 )
238 if qs.exists():
239 last_issue = qs.first()
240 if container.pid == last_issue.pid:
241 context["last_issue"] = True
243 context["bs_version"] = (
244 settings.BOOTSTRAP_VERSION if hasattr(settings, "BOOTSTRAP_VERSION") else 3
245 )
246 if container.ctype == "issue_special" and container.resources_in_special_issue: 246 ↛ 247line 246 didn't jump to line 247 because the condition on line 246 was never true
247 context["resource_in_special_issue"] = [
248 r.resource for r in container.resources_in_special_issue.all()
249 ]
251 update_context_with_desc(context)
254# def views_get_container(request, container, api=False, *args, **kwargs):
255# context = {}
256# views_update_container_context(request, container, context)
257# template = context['template']
258# return render(request, template, context)
261# class ContainerView(TemplateView):
262# template_name = ''
263#
264# def get_context_data(self, **kwargs):
265# context = super(ContainerView, self).get_context_data(**kwargs)
266#
267# if 'pid' in kwargs:
268# pid = kwargs['pid']
269# else:
270# pid = self.kwargs.get('pid')
271# container = model_helpers.get_container(pid)
272# if pid == getattr(settings, 'ISSUE_TO_APPEAR_PID', 'XXX') and not container:
273# self.template_name = 'blocks/issue_detail_empty.html'
274# else:
275# views_update_container_context(self.request, container, context)
276# self.template_name = context['template']
277#
278# return context
281# def views_get_article(
282# request: HttpRequest,
283# article: Optional[Article],
284# api=False,
285# lang=None,
286# *args,
287# **kwargs
288# ):
289#
290#
291# return render(request, template, context)
294# @require_http_methods(["GET"])
295# def article(request: HttpRequest, aid: str, api=False):
296# return ItemView.as_view()(request)
297#
298#
299# @require_http_methods(["GET"])
300# def article_lang(request, lang, aid, api=False):
301# return ItemView.as_view()(request)
304class ItemView(TemplateView):
305 def __init__(self, *args, **kwargs):
306 super().__init__(*args, **kwargs)
307 self.template_name = ""
308 self.obj = None
309 self.pid = None
310 self.display_lang = None
312 def get_obj(self, **kwargs):
313 self.pid = self.kwargs.get("aid", None)
314 if self.pid is None:
315 self.pid = self.kwargs.get("pid", None)
316 if self.pid is None: 316 ↛ 317line 316 didn't jump to line 317 because the condition on line 316 was never true
317 self.pid = self.request.GET.get("id", "").replace("/", "")
318 if "\x00" in self.pid: 318 ↛ 319line 318 didn't jump to line 319 because the condition on line 318 was never true
319 raise Http404
321 if "/" in self.pid:
322 # If someone types /articles/10.5802/crgeos.150-fr, we set the display lang and get 10.5802/crgeos.150
323 if not self.display_lang and self.pid[-3] == "-" and not self.pid[-2:].isdigit(): 323 ↛ 324line 323 didn't jump to line 324 because the condition on line 323 was never true
324 self.display_lang = self.pid[-2:]
325 self.pid = self.pid[:-3]
327 obj = model_helpers.get_article_by_doi(self.pid, prefetch=True)
328 else:
329 obj = model_helpers.get_resource(self.pid, prefetch=True)
330 if obj is not None:
331 obj = obj.cast()
332 fct = getattr(model_helpers, "get_" + obj.classname.lower())
333 if fct and callable(fct): 333 ↛ 336line 333 didn't jump to line 336 because the condition on line 333 was always true
334 obj = fct(self.pid, prefetch=True)
336 if obj is None and self.pid != getattr(settings, "ISSUE_TO_APPEAR_PID", "XXX"):
337 raise Http404
339 self.obj = obj
341 def get_context_data(self, **kwargs):
342 context = super().get_context_data(**kwargs)
343 context["journal"] = self.obj.get_collection()
344 context["obj"] = self.obj
345 return context
347 def get(self, request, *args, **kwargs):
348 if "obj" in kwargs:
349 self.obj = kwargs["obj"]
350 if "lang" in kwargs: 350 ↛ 351line 350 didn't jump to line 351 because the condition on line 350 was never true
351 self.display_lang = kwargs["lang"]
353 if (
354 self.obj is not None
355 ): # ItemView may call ArticleView. Avoid infinite loop is self.obj is set
356 return super().get(request, *args, **kwargs)
358 self.get_obj(**kwargs)
359 if not self.display_lang: 359 ↛ 361line 359 didn't jump to line 361 because the condition on line 359 was always true
360 self.display_lang = self.kwargs.get("lang", None)
361 view_cls = ItemViewClassFactory().get_resource_view(self.obj.classname)
362 if view_cls is not None: 362 ↛ 367line 362 didn't jump to line 367 because the condition on line 362 was always true
363 kwargs["obj"] = self.obj
364 if self.display_lang is not None: 364 ↛ 365line 364 didn't jump to line 365 because the condition on line 364 was never true
365 kwargs["lang"] = self.display_lang
366 return view_cls.as_view()(request, *args, **kwargs)
367 return super().get(request, *args, **kwargs)
370class ArticleView(ItemView):
371 def get_context_data(self, **kwargs):
372 context = super().get_context_data(**kwargs)
373 request = self.request
374 display_lang = self.display_lang
375 article = self.obj
377 article.title_tex = article.title_tex.strip()
378 source = request.GET.get("source", None)
379 container = article.my_container
380 collection = container.my_collection
381 citing_articles = article.citations()
383 with_tex = (
384 "formula_display" in request.session and request.session["formula_display"] == "tex"
385 )
387 if display_lang is not None and len(display_lang) == 2: 387 ↛ 388line 387 didn't jump to line 388 because the condition on line 387 was never true
388 context["display_lang"] = display_lang
389 else:
390 display_lang = context["display_lang"] = article.lang
392 context["collection"] = collection
393 context["journal"] = collection
394 context["is_translation"] = False
395 context["full_translation"] = False
396 context["needs_translation"] = True
397 context["start_translation_url"] = os.path.join(
398 settings.TRANSLATION_URL, f"translation/new?doi={article.doi}"
399 )
400 langs = [article.lang] if (article.lang and article.lang != "und") else []
401 # if article.trans_lang and article.trans_lang != 'und':
402 # langs.append(article.trans_lang)
403 for translated_article in article.translations.all(): 403 ↛ 404line 403 didn't jump to line 404 because the loop on line 403 never started
404 if translated_article.lang not in langs:
405 langs.append(translated_article.lang)
406 if display_lang == translated_article.lang:
407 context["is_translation"] = True
408 context["needs_translation"] = False
409 context["translated_article"] = translated_article
410 context["body_html"] = translated_article.body_html
412 context["languages"] = langs
414 if display_lang == article.lang or ( 414 ↛ 424line 414 didn't jump to line 424 because the condition on line 414 was always true
415 not context["is_translation"] and display_lang != article.trans_lang
416 ):
417 context["article_title"] = article.title_tex if with_tex else article.title_html
418 context["body_html"] = article.body_html
419 if display_lang == article.lang: 419 ↛ 421line 419 didn't jump to line 421 because the condition on line 419 was always true
420 context["needs_translation"] = False
421 for abstract in article.get_abstracts(): 421 ↛ 422line 421 didn't jump to line 422 because the loop on line 421 never started
422 if abstract.lang == display_lang:
423 context["abstract"] = abstract
424 elif display_lang == article.trans_lang or not context["is_translation"]:
425 context["article_title"] = (
426 article.trans_title_tex if with_tex else article.trans_title_html
427 )
428 context["article_original_title"] = (
429 article.title_tex if with_tex else article.title_html
430 )
431 if "body_html" not in context or not context["body_html"]:
432 # The full text is not available in the display_lang. Use the original version.
433 context["body_html"] = article.body_html
434 for abstract in article.get_abstracts():
435 if abstract.lang == display_lang:
436 context["abstract"] = abstract
437 else:
438 context["full_translation"] = True
439 context["article_original_title"] = (
440 article.title_tex if with_tex else article.title_html
441 )
442 for translated_article in article.translations.all():
443 if display_lang == translated_article.lang:
444 context["article_title"] = (
445 translated_article.title_tex if with_tex else translated_article.title_html
446 )
447 context["abstract"] = translated_article.get_abstracts().first()
449 if context["is_translation"]: 449 ↛ 450line 449 didn't jump to line 450 because the condition on line 449 was never true
450 context["howtocite"] = translated_article.get_citation(request)
451 else:
452 context["howtocite"] = article.get_citation(request)
454 if ( 454 ↛ 458line 454 didn't jump to line 458
455 hasattr(settings, "ISSUE_TO_APPEAR_PID")
456 and settings.ISSUE_TO_APPEAR_PID == container.pid
457 ):
458 context["articles_to_appear"] = True
460 # Format comment URL if comments are activated
461 if getattr(settings, "COMMENTS_VIEWS_ARTICLE_COMMENTS", False) is True: 461 ↛ 462line 461 didn't jump to line 462 because the condition on line 461 was never true
462 current_url = f"{request.build_absolute_uri()}#article-comments-section"
463 query_params = {"redirect_url": current_url}
464 # Forward some query parameters if present
465 preview_id = request.GET.get(PARAM_PREVIEW)
466 if preview_id:
467 query_params[PARAM_PREVIEW] = preview_id
468 comment_reply_to = request.GET.get("commentReplyTo")
469 if comment_reply_to:
470 query_params["commentReplyTo"] = comment_reply_to
472 context["comments_url"] = format_url_with_params(
473 reverse("article_comments", kwargs={"doi": article.doi}), query_params
474 )
476 is_cr = False
477 if ( 477 ↛ 485line 477 didn't jump to line 485
478 hasattr(settings, "SITE_NAME")
479 and len(settings.SITE_NAME) == 6
480 and settings.SITE_NAME[0:2] == "cr"
481 ):
482 context["is_cr"] = is_cr = True
483 context["related_articles"] = get_suggested_articles(article)
485 context["online_first_cr"] = False
486 if container.with_online_first and is_cr:
487 context["articles_to_appear"] = True
488 context["online_first_cr"] = True
490 if hasattr(settings, "SHOW_BODY") and settings.SHOW_BODY: 490 ↛ 491line 490 didn't jump to line 491 because the condition on line 490 was never true
491 context["article_show_body"] = True
493 context["breadcrumb"] = breadcrumb.get_breadcrumb(article)
494 context["btn_show_tex"] = settings.SHOW_TEX if hasattr(settings, "SHOW_TEX") else False
496 # pour les livres du centre Mersenne, on remplace la collection principale par collectionmembership
497 if collection.pid == "MBK": 497 ↛ 498line 497 didn't jump to line 498 because the condition on line 497 was never true
498 for collection_membership in container.collectionmembership_set.all():
499 collection = collection_membership.collection
500 container.my_collection = collection
502 if container.ctype == "issue": 502 ↛ 512line 502 didn't jump to line 512 because the condition on line 502 was always true
503 context.update(
504 {
505 "article": article,
506 "citing_articles": citing_articles,
507 "source": source,
508 }
509 )
510 context["template"] = "blocks/article.html"
511 else:
512 context.update(
513 {"book_part": article, "citing_articles": citing_articles, "source": source}
514 )
515 context["template"] = "blocks/book-part.html"
517 if hasattr(settings, "SITE_REGISTER"): 517 ↛ 535line 517 didn't jump to line 535 because the condition on line 517 was always true
518 site_entry = settings.SITE_REGISTER[settings.SITE_NAME]
520 licence = None
521 try:
522 year = int(article.my_container.year)
523 except ValueError:
524 year = 0
526 if "licences" in site_entry: 526 ↛ 533line 526 didn't jump to line 533 because the condition on line 526 was always true
527 licences = site_entry["licences"]
528 i = len(licences) - 1
529 while not licence and i >= 0:
530 if year >= licences[i][0]:
531 licence = licences[i][1]
532 i -= 1
533 context["licence"] = licence
535 context["coltype"] = collection.coltype
536 context["supplementary_materials"] = models.SupplementaryMaterial.objects.filter(
537 relatedobject_ptr__resource__pk=article.pk,
538 ).filter(rel="supplementary-material")
539 context["reviews"] = models.SupplementaryMaterial.objects.filter(
540 relatedobject_ptr__resource__pk=article.pk,
541 ).filter(rel="review")
542 context["bibtex"] = article.get_bibtex(request)
543 context["ris"] = article.get_ris(request)
544 context["enw"] = article.get_endnote(request)
545 link = model_helpers.get_extlink(resource=collection, rel="test_website")
546 context["test_website"] = link.location if link else None
547 link = model_helpers.get_extlink(resource=collection, rel="website")
548 context["prod_website"] = link.location if link else None
549 context["bs_version"] = (
550 settings.BOOTSTRAP_VERSION if hasattr(settings, "BOOTSTRAP_VERSION") else 3
551 )
552 context["recommendations"] = article.extid_set.filter(id_type="rdoi")
553 if article.used_in_special_issues.all(): 553 ↛ 554line 553 didn't jump to line 554 because the condition on line 553 was never true
554 context["special_issues"] = [
555 si.my_container for si in article.used_in_special_issues.all()
556 ]
557 # context["virtual_issues"] = container
558 self.template_name = context["template"]
560 return context
563class ContainerView(ItemView):
564 def get_context_data(self, **kwargs):
565 context = super().get_context_data(**kwargs)
567 if self.pid == getattr(settings, "ISSUE_TO_APPEAR_PID", "XXX") and not self.obj: 567 ↛ 568line 567 didn't jump to line 568 because the condition on line 567 was never true
568 self.template_name = "blocks/issue_detail_empty.html"
569 else:
570 container = self.obj
571 if container is None: 571 ↛ 572line 571 didn't jump to line 572 because the condition on line 571 was never true
572 raise Http404
574 views_update_container_context(self.request, container, context, self.display_lang)
576 self.template_name = context["template"]
578 return context
581class CollectionView(ItemView):
582 def get(self, request, *args, **kwargs):
583 if "obj" in kwargs: 583 ↛ 586line 583 didn't jump to line 586 because the condition on line 583 was always true
584 self.obj = kwargs["obj"]
586 collection = self.obj
587 if collection.coltype in ["journal", "acta"]:
588 return IssuesView.as_view()(request, jid=collection.pid)
589 elif collection.coltype in ["lecture-notes"]: 589 ↛ 590line 589 didn't jump to line 590 because the condition on line 589 was never true
590 request.META["QUERY_STRING"] = '"' + collection.title_html + '"-p'
591 return sorted_lectures_detailed_view(request)
592 elif collection.coltype == "proceeding": 592 ↛ 593line 592 didn't jump to line 593 because the condition on line 592 was never true
593 url = reverse("proceeding-issues", kwargs={"jid": collection.pid})
594 return HttpResponseRedirect(url)
595 # return proceedings(request, jid=collection.pid)
596 else:
597 # en theorie: coltype=book-series TODO: check lectures
598 request.META["QUERY_STRING"] = '"' + collection.title_html + '"-p'
599 return sorted_books(request)
602class VolumeDetailView(TemplateView):
603 template_name = "blocks/volume-items.html"
605 def get_context_data(self, **kwargs):
606 # TODO next_volume, previous_volume
607 context = super().get_context_data(**kwargs)
609 context["group_issues"] = False
610 is_cr = (
611 hasattr(settings, "SITE_NAME")
612 and len(settings.SITE_NAME) == 6
613 and settings.SITE_NAME[0:2] == "cr"
614 )
615 context["is_cr"] = is_cr
617 issues_articles, collection = model_helpers.get_issues_in_volume(kwargs.get("vid"), is_cr)
618 year = int(issues_articles[0]["issue"].year.split("-")[0])
619 context["year"] = year
621 if is_cr:
622 context["group_issues"] = (settings.SITE_NAME != "crbiol" and year > 2020) or (
623 settings.SITE_NAME == "crbiol" and year > 2022
624 )
626 with_thematic = (
627 len(issues_articles) > 1 and len(issues_articles[1]["issue"].title_html) > 0
628 )
629 context["with_thematic"] = with_thematic
631 # Olivier 7/30/2020. Le code suivant crash lorsque fpage n'est pas un entier mais un chiffre romain
632 # Il faut rediscuter des specs. Ordonner par 'seq' semble plus simple.
633 # Il faut peut-être juste s'assurer que 'seq' soit bien mis à l'import ?
635 # Ex: /volume/WBLN_2018__5/
636 # issues_articles = []
637 # for issue in issues:
638 # articles = issue.article_set.all().annotate(
639 # fpage_int=Cast(
640 # Case(When(~Q(fpage=''), then='fpage'), default=Value('0')),
641 # IntegerField()
642 # )
643 # ).order_by('seq', 'fpage_int')
644 #
645 # issues_articles.append({'issue': issue, 'articles': articles})
647 context["issues_articles"] = issues_articles
648 context["collection"] = collection
649 context["journal"] = collection
650 context["coltype"] = collection.coltype
651 context["breadcrumb"] = breadcrumb.get_breadcrumb(
652 issues_articles[-1].get("issue"), is_volume=True
653 )
654 context["btn_show_tex"] = settings.SHOW_TEX if hasattr(settings, "SHOW_TEX") else False
656 if collection.pid == "ART": 656 ↛ 657line 656 didn't jump to line 657 because the condition on line 656 was never true
657 now = timezone.now()
658 curyear = now.year
659 context["volume_in_progress"] = year == curyear
661 return context
664class VolumeGeneralDetailView(TemplateView):
665 template_name = "blocks/volume-general-items.html"
667 def get_context_data(self, **kwargs):
668 # TODO next_volume, previous_volume
669 context = super().get_context_data(**kwargs)
671 context["group_issues"] = False
672 is_cr = (
673 hasattr(settings, "SITE_NAME")
674 and len(settings.SITE_NAME) == 6
675 and settings.SITE_NAME[0:2] == "cr"
676 )
677 context["is_cr"] = is_cr
679 issues_articles, collection = model_helpers.get_issues_in_volume(
680 kwargs.get("vid"), is_cr, general_articles=True
681 )
683 if is_cr:
684 year = int(issues_articles[0]["issue"].year)
685 context["group_issues"] = (settings.SITE_NAME != "crbiol" and year > 2020) or (
686 settings.SITE_NAME == "crbiol" and year > 2022
687 )
689 with_thematic = (
690 len(issues_articles) > 1 and len(issues_articles[1]["issue"].title_html) > 0
691 )
692 context["with_thematic"] = with_thematic
694 context["issues_articles"] = issues_articles
695 context["collection"] = collection
696 context["journal"] = collection
697 context["coltype"] = collection.coltype
698 context["breadcrumb"] = breadcrumb.get_breadcrumb(issues_articles[-1].get("issue"))
699 context["btn_show_tex"] = settings.SHOW_TEX if hasattr(settings, "SHOW_TEX") else False
700 context["vid"] = kwargs.get("vid")
701 # for article in issues_articles:
702 # for a in article["articles"]:
703 # special = a.used_in_special_issues.all()
704 # for s in special:
705 # context["in_special"] = vars(s)
706 # pass
707 # pass
708 context["request"] = self.request
710 return context
713class ItemViewClassFactory:
714 views = {
715 "article": ArticleView,
716 "container": ContainerView,
717 "collection": CollectionView,
718 "volume": VolumeDetailView,
719 }
721 def get_resource_view(self, classname):
722 classname = classname.lower()
723 if classname in self.views: 723 ↛ 725line 723 didn't jump to line 725 because the condition on line 723 was always true
724 return self.views[classname]
725 return None
728@require_http_methods(["GET"])
729def journals(request, api=False):
730 journals = model_helpers.get_journals().filter(parent=None).order_by("title_tex")
732 context = {"journals": journals, "coltype": "journal"}
733 context["journal"] = model_helpers.get_collection(settings.COLLECTION_PID)
735 return render(request, "blocks/journal-list.html", context)
738@require_http_methods(["GET"])
739def actas(request, api=False):
740 journals = model_helpers.get_actas().filter(parent=None)
741 context = {"journals": journals, "coltype": "acta"}
743 return render(request, "blocks/journal-list.html", context)
746@require_http_methods(["GET"])
747def books(request, api=False):
748 books = model_helpers.get_collection_of_books().order_by("title_tex")
749 context = {"journals": books, "coltype": "book"}
751 return render(request, "blocks/journal-list.html", context)
754@require_http_methods(["GET"])
755def proceedings(request):
756 proceedings = model_helpers.get_proceedings().order_by("title_tex")
757 context = {"journals": proceedings, "coltype": "proceeding"}
759 return render(request, "blocks/journal-list.html", context)
762@require_http_methods(["GET"])
763def thesis(request):
764 if request.GET: # i.e. /thesis?<search_term>
765 return thesis_detailed_view(request)
766 else: # /thesis
767 thesis = model_helpers.get_collection_of_thesis().order_by("title_tex")
768 context = {"journals": thesis, "coltype": "thesis"}
770 return render(request, "blocks/journal-list.html", context)
773@require_http_methods(["GET"])
774def sorted_lectures(request): # i.e. /lectures?<search_term>
775 if request.GET:
776 return sorted_lectures_detailed_view(request)
777 else: # /lectures
778 lectures = model_helpers.get_lectures().order_by("title_tex")
779 context = {"journals": lectures, "coltype": "lectures"}
781 return render(request, "blocks/journal-list.html", context)
784@require_http_methods(["GET"])
785def thesis_detailed_view(request, sorted_by="fau", order_by="asc", api=False):
786 # request.path like : thesis?C-"Thèses de l'entre-deux-guerres"-rp/
788 decoded_query_string = decode_query_string(request)
790 path, queryDict = CleanSearchURL.decode(decoded_query_string, "/thesis")
792 request.GET = queryDict
793 request.path = path
794 request.path_info = path
796 filters = []
797 filters = request.GET.getlist("f")
799 path = request.path
800 search_path = path + "?"
802 try:
803 page = int(request.GET.get("page", 1))
804 except ValueError:
805 page = 1
807 params = {
808 "q": '(classname:"Thèse")',
809 "sorted_by": sorted_by,
810 "order_by": order_by,
811 "page": page,
812 "filters": filters,
813 "path": path,
814 "search_path": search_path,
815 "facet_fields": ["firstLetter", "year_facet", "collection_title_facet"],
816 "query": decoded_query_string,
817 }
819 return generic_books(request, "thesis", params)
822@require_http_methods(["GET"])
823def sorted_lectures_detailed_view(
824 request, collection=None, sorted_by="fau title_sort_key", order_by="asc", api=False
825):
826 decoded_query_string = decode_query_string(request)
828 path, queryDict = CleanSearchURL.decode(decoded_query_string, "/lectures")
830 request.GET = queryDict
831 request.path = path
832 request.path_info = path
834 filters = []
835 filters = request.GET.getlist("f")
837 collection = model_helpers.get_collection(collection)
839 path = request.path
840 search_path = path + "?"
842 try:
843 page = int(request.GET.get("page", 1))
844 except ValueError:
845 page = 1
847 params = {
848 "q": '(classname:"Notes de cours")',
849 "sorted_by": sorted_by,
850 "order_by": order_by,
851 "page": page,
852 "filters": filters,
853 "path": path,
854 "search_path": search_path,
855 "facet_fields": ["firstLetter", "year_facet", "collection_title_facet"],
856 "query": decoded_query_string,
857 }
858 return generic_books(request, "lectures", params)
861class IssuesView(TemplateView):
862 template_name = "blocks/issue-list.html"
864 def get_context_data(self, **kwargs):
865 context = super().get_context_data(**kwargs)
867 journal = model_helpers.get_collection(self.kwargs.get("jid"), sites="")
869 if journal is None: 869 ↛ 870line 869 didn't jump to line 870 because the condition on line 869 was never true
870 raise Http404
871 try:
872 current_edition = journal.extlink_set.get(rel="website").location
873 if "numdam.org" in current_edition:
874 current_edition = None
875 except models.ExtLink.DoesNotExist:
876 current_edition = None
878 result = model_helpers.get_volumes_in_collection(journal)
879 volume_count = result["volume_count"]
880 collections = []
881 for ancestor in journal.ancestors.all(): 881 ↛ 882line 881 didn't jump to line 882 because the loop on line 881 never started
882 item = model_helpers.get_volumes_in_collection(ancestor)
883 volume_count = max(0, volume_count)
884 item.update({"journal": ancestor})
885 collections.append(item)
887 # add the parent collection to its children list and sort it by date
888 result.update({"journal": journal})
889 collections.append(result)
890 if len(collections) > 1: 890 ↛ 891line 890 didn't jump to line 891 because the condition on line 890 was never true
891 collections.sort(
892 key=lambda ancestor: ancestor["sorted_issues"][0]["volumes"][0]["lyear"],
893 reverse=True,
894 )
896 is_cr = False
897 if ( 897 ↛ 902line 897 didn't jump to line 902
898 hasattr(settings, "SITE_NAME")
899 and len(settings.SITE_NAME) == 6
900 and settings.SITE_NAME[0:2] == "cr"
901 ):
902 is_cr = True
904 display_with_titles = True
905 i = 0
906 while display_with_titles and i < len(collections):
907 col = collections[i]
908 if len(col["sorted_issues"]) != 1: 908 ↛ 909line 908 didn't jump to line 909 because the condition on line 908 was never true
909 display_with_titles = False
910 else:
911 j = 0
912 volumes = col["sorted_issues"][0]["volumes"]
913 while display_with_titles and j < len(volumes):
914 vol = volumes[j]
915 display_with_titles = len(vol["issues"]) == 1
916 if display_with_titles: 916 ↛ 919line 916 didn't jump to line 919 because the condition on line 916 was always true
917 issue = vol["issues"][0]
918 display_with_titles = issue.title_tex != ""
919 j += 1
920 i += 1
921 # for vserie in result["sorted_issues"]:
922 # for volume in vserie["volumes"]:
923 # print(volume["issues"][0])
924 # if volume["issues"][0].ctype == "issue_special":
925 # volume["issues"].reverse()
926 # continue
928 context.update(
929 {
930 "obj": journal,
931 "journal": journal,
932 "sorted_issues": result["sorted_issues"],
933 "coltype": journal.coltype,
934 "volume_count": volume_count,
935 "max_width": result["max_width"],
936 "to_appear": models.Article.objects.filter(pid__startswith=f"{journal.pid}_0"),
937 "show_issues": True,
938 "is_cr": is_cr,
939 "current_edition": current_edition,
940 "collections": collections,
941 "display_with_titles": display_with_titles,
942 }
943 )
944 update_context_with_desc(context)
946 return context
949def generic_books(request, coltype, params):
950 rows = 20
951 start = (params["page"] - 1) * rows
953 params["rows"] = 20
954 params["start"] = start
955 if " " in params["sorted_by"]: 955 ↛ 959line 955 didn't jump to line 959 because the condition on line 955 was always true
956 sorts = params["sorted_by"].split()
957 params["sort"] = ",".join([f"{item} {params['order_by']}" for item in sorts])
958 else:
959 params["sort"] = params["sorted_by"] + " " + params["order_by"]
961 cmd = solr_cmds.solrInternalSearchCmd(params)
962 results = cmd.do()
963 # si len(results.facets['collection_title_facets']) == 1,
964 # on est dans le cas où une collection est choisie
966 context = {"results": results, "coltype": coltype}
968 if results is not None: 968 ↛ 995line 968 didn't jump to line 995 because the condition on line 968 was always true
969 if results.facets["collection_title_facets"]: 969 ↛ 970line 969 didn't jump to line 970 because the condition on line 969 was never true
970 has_one_collection_filter = False
971 collection_title_filter = ""
972 if "filters" in params:
973 for filter_ in params["filters"]:
974 if filter_.find("collection_title_facet:") > -1:
975 has_one_collection_filter = (
976 True if not has_one_collection_filter else False
977 )
978 collection_title_filter = filter_.split('"')[1]
980 if has_one_collection_filter:
981 obj = model_helpers.get_resource(results.docs[0]["pid"])
982 if obj:
983 book = obj.cast()
984 container = book.get_container()
985 collection = container.get_collection()
986 if collection.title_tex == collection_title_filter:
987 context["collection"] = collection
988 else:
989 for other_collection in container.my_other_collections.all():
990 if other_collection.title_tex == collection_title_filter:
991 context["collection"] = other_collection
993 context.update(utils.paginate_from_request(request, params, results.hits))
995 context["query"] = params["query"]
996 if settings.SITE_NAME == "numdam": 996 ↛ 997line 996 didn't jump to line 997 because the condition on line 996 was never true
997 context["numdam"] = True
998 return render(request, "blocks/sorted-books.html", context)
1001@require_http_methods(["GET"])
1002def sorted_books(request, sorted_by="fau title_sort_key", order_by="asc", api=False):
1003 decoded_query_string = decode_query_string(request)
1004 path, queryDict = CleanSearchURL.decode(decoded_query_string, "/series")
1006 request.GET = queryDict
1007 request.path = path
1008 request.path_info = path
1010 filters = []
1011 filters = request.GET.getlist("f")
1013 path = request.path
1014 search_path = path + "?"
1016 try:
1017 page = int(request.GET.get("page", 1))
1018 except ValueError:
1019 page = 1
1021 params = {
1022 "q": '(classname:"Livre")',
1023 "sorted_by": sorted_by,
1024 "order_by": order_by,
1025 "page": page,
1026 "filters": filters,
1027 "path": path,
1028 "search_path": search_path,
1029 "facet_fields": ["firstLetter", "year_facet", "collection_title_facet"],
1030 "query": decoded_query_string,
1031 }
1032 return generic_books(request, "book-series", params)
1035@method_decorator(csrf_exempt, name="dispatch")
1036class SearchView(TemplateView):
1037 template_name = "blocks/search-results.html"
1038 GET = None
1039 path = None
1041 def get(self, request, *args, **kwargs):
1042 decoded_query_string = decode_query_string(request)
1043 path = self.kwargs.get("path")
1044 # query like test-"aitre, -ert"-qa
1045 path, queryDict = CleanSearchURL.decode(decoded_query_string, path)
1046 self.GET = queryDict
1047 self.path = path
1048 try:
1049 result = super().get(request, *args, **kwargs)
1050 except SuspiciousOperation:
1051 result = HttpResponseBadRequest("Bad request")
1053 return result
1055 def post(self, request, *args, **kwargs):
1056 if request.POST.get("q0", "") == "":
1057 return HttpResponseRedirect(reverse("help"))
1059 show_eprint = request.POST.get("show_eprint", False)
1060 show_eprint = True if show_eprint == "on" else show_eprint
1061 path = CleanSearchURL.encode(request.POST, request.path)
1062 path += "&" + urllib.parse.urlencode({"eprint": show_eprint})
1064 return HttpResponseRedirect(path)
1066 def get_context_data(self, **kwargs):
1067 context = super().get_context_data(**kwargs)
1069 raw_query_string = self.request.META.get("QUERY_STRING", "")
1070 if raw_query_string:
1071 decoded_query_string = urllib.parse.unquote(raw_query_string)
1072 parse = urllib.parse.parse_qs(decoded_query_string)
1073 context["query"] = decoded_query_string
1074 eprint = False
1076 try:
1077 if "eprint" in parse:
1078 eprint = eval(parse["eprint"][0])
1079 cache.set("eprint", eprint, 60 * 60 * 24 * 2)
1080 except:
1081 value = cache.get("eprint")
1082 eprint = False if not value else value
1083 finally:
1084 cache.close()
1086 filters = self.GET.getlist("f")
1088 if eprint is False: 1088 ↛ 1091line 1088 didn't jump to line 1091 because the condition on line 1088 was always true
1089 filters.append('!dt:"e-print"')
1091 search_path = self.path + "?"
1092 qs = []
1094 keep_qs_in_display = True
1095 i = 0
1096 qti = self.GET.get("qt" + str(i), None)
1098 while qti:
1099 if i > 0:
1100 search_path += "&"
1101 search_path += "qt" + str(i) + "=" + qti
1103 if qti == "date":
1104 qfi = self.GET.get("q-f-" + str(i), None)
1105 qli = self.GET.get("q-l-" + str(i), None)
1107 if qfi or qli: 1107 ↛ 1132line 1107 didn't jump to line 1132 because the condition on line 1107 was always true
1108 qs.append({"name": qti, "first": qfi, "last": qli, "value": "", "not": False})
1110 search_path += "&q-f-" + str(i) + "=" + qfi
1111 search_path += "&q-l-" + str(i) + "=" + qli
1112 else:
1113 if qti == "author_ref":
1114 keep_qs_in_display = False
1116 qi = self.GET.get("q" + str(i), None)
1118 if qi or i == 0: 1118 ↛ 1132line 1118 didn't jump to line 1132 because the condition on line 1118 was always true
1119 noti = self.GET.get("not" + str(i), None)
1120 if noti == "on":
1121 noti = True
1122 else:
1123 noti = False
1125 qs.append({"name": qti, "value": qi, "not": noti, "first": "", "last": ""})
1127 search_path += "&q" + str(i) + "=" + qi
1129 if noti:
1130 search_path += "¬" + str(i) + "=on"
1132 i += 1
1133 qti = self.GET.get("qt" + str(i), None)
1135 try:
1136 page = int(self.GET.get("page", 1))
1137 except ValueError:
1138 page = 1
1140 if len(qs) < 1:
1141 # 400.html is used only if DEBUG is False
1142 # (see get_response in django.core.handlers.base)
1143 raise SuspiciousOperation("Bad request")
1145 rows = 20
1146 start = (page - 1) * rows
1148 params = {
1149 "filters": filters,
1150 "qs": qs,
1151 "page": page,
1152 "start": start,
1153 "rows": rows,
1154 "search_path": search_path,
1155 "path": self.path,
1156 }
1158 sort = self.GET.get("order_by")
1159 if sort: 1159 ↛ 1160line 1159 didn't jump to line 1160 because the condition on line 1159 was never true
1160 params["sort"] = sort
1162 cmd = solr_cmds.solrSearchCmd(params)
1163 results = cmd.do()
1165 if not keep_qs_in_display:
1166 qs = []
1168 context.update({"results": results, "qs": qs, "eprint": eprint})
1170 if results is not None: 1170 ↛ 1173line 1170 didn't jump to line 1173 because the condition on line 1170 was always true
1171 context.update(utils.paginate_from_request(self, params, results.hits))
1173 context["journal"] = model_helpers.get_collection(settings.COLLECTION_PID)
1175 return context
1178@require_http_methods(["GET"])
1179def authors(request, api=False):
1180 """
1181 @param request: like /authors?q=M+r
1182 @param api:
1183 @return:
1184 """
1186 decoded_query_string = decode_query_string(request)
1187 path, queryDict = CleanSearchURL.decode(decoded_query_string, "/authors")
1189 request.GET = queryDict
1190 request.path = path
1191 request.path_info = path
1193 # to set default value on letter
1194 letter = request.GET.get("letter", "A")[0]
1195 filters = request.GET.getlist("f")
1196 for filter_ in filters: 1196 ↛ 1197line 1196 didn't jump to line 1197 because the loop on line 1196 never started
1197 if filter_.startswith("{!tag=firstletter}firstNameFacetLetter:"):
1198 letter = filter_[39]
1200 try:
1201 page = int(request.GET.get("page", 1))
1202 except ValueError:
1203 page = 1
1205 rows = 60
1207 all_authors = model_helpers.get_authors_by_letter(letter)
1208 paginator = Paginator(all_authors, rows)
1209 try:
1210 authors = paginator.page(page)
1211 except EmptyPage:
1212 raise Http404
1214 context = {
1215 "authors": authors,
1216 "letters": string.ascii_uppercase,
1217 "letter_active": letter,
1218 "query": decoded_query_string,
1219 }
1221 params = {"rows": rows, "page": page, "path": path}
1222 context.update(utils.paginate_from_request(request, params, paginator.count))
1224 return render(request, "blocks/authors.html", context)
1227@require_http_methods(["GET"])
1228def citations(request, aid, api=False):
1229 resource = model_helpers.get_resource(aid)
1230 if resource is None or (resource.classname != "Article" and resource.classname != "Container"):
1231 raise Http404
1232 # a priori les citations ne sont que sur les articles/book
1233 # resource.classname != 'Article'-part : et ben non !
1234 item = resource.cast()
1235 citing = item.citations()
1236 context = {"resource": item}
1237 context["citing"] = citing
1238 return render(request, "blocks/resource-citations.html", context)
1241@require_http_methods(["GET"])
1242def get_pdf(request, pid, binary_file_type, extension, relative_path):
1243 """
1244 return PDF file of an Resource or of a RelatedObject for Container if embargo not present
1245 si l'item n'existe pas : 404
1246 si il est protege par barriere mobile : alors on renvoie vers
1247 une page qui redirige apres un delai vers la notice de l'objet
1248 """
1250 return get_binary_file(request, pid, binary_file_type, extension, relative_path)
1253@require_http_methods(["GET"])
1254def get_binary_file(request, pid, binary_file_type, extension, relative_path):
1255 """
1256 return tex file of a Resource if embargo not present
1257 return 404 if the file or the resource does not exist
1258 If there is an embargo redirect to the resource page
1259 """
1261 if len(relative_path) > 0: 1261 ↛ 1262line 1261 didn't jump to line 1262 because the condition on line 1261 was never true
1262 extension = relative_path.split(".")[-1]
1264 type_extension = {
1265 "pdf": "application/pdf",
1266 "djvu": "image/x.djvu",
1267 "tex": "application/x-tex",
1268 "png": "image/png",
1269 "jpg": "image/jpeg",
1270 "html": "text/html",
1271 }
1272 mimetype = type_extension.get(extension, "")
1274 # binary_file_type: 'toc', 'frontmatter', 'backmatter', or 'self' for
1275 # the article/issue binary file
1276 if not binary_file_type:
1277 binary_file_type = "self"
1279 if "/" in pid: 1279 ↛ 1280line 1279 didn't jump to line 1280 because the condition on line 1279 was never true
1280 resource = model_helpers.get_resource_by_doi(pid)
1281 else:
1282 resource = model_helpers.get_resource(pid)
1284 if resource is not None: 1284 ↛ 1287line 1284 didn't jump to line 1287 because the condition on line 1284 was always true
1285 resource = resource.cast()
1287 filename, status = get_binary_filename(resource, relative_path, binary_file_type, mimetype)
1288 return render_binary_file(request, resource, status, filename)
1291def get_binary_filename(resource, relative_path, binary_file_type, mimetype):
1292 """
1293 get the filename from the database
1294 returns the filename and the status 200, 404 (not found) or 403 (embargo)
1295 """
1296 if resource is None: 1296 ↛ 1297line 1296 didn't jump to line 1297 because the condition on line 1296 was never true
1297 return None, 404
1299 allow_local_pdf = not hasattr(settings, "ALLOW_LOCAL_PDF") or settings.ALLOW_LOCAL_PDF
1300 if not allow_local_pdf: 1300 ↛ 1301line 1300 didn't jump to line 1301 because the condition on line 1300 was never true
1301 return None, 404
1303 filename = None
1304 status = 200
1306 try:
1307 # May return None if there is an embargo
1308 filename = resource.get_binary_disk_location(binary_file_type, mimetype, relative_path)
1309 except exceptions.ResourceDoesNotExist:
1310 status = 404
1312 # File is protected
1313 if filename is None:
1314 status = 403
1316 return filename, status
1319def render_binary_file(request, resource, status, filename):
1320 if status == 404: 1320 ↛ 1321line 1320 didn't jump to line 1321 because the condition on line 1320 was never true
1321 raise Http404
1322 elif status == 403:
1323 template_name = "403withRedirect.html"
1324 response = render(request, template_name, {"pid": resource.pid}, status=403)
1325 else:
1326 full_filename = os.path.join(settings.RESOURCES_ROOT, filename)
1328 try:
1329 file_ = open(full_filename, "rb")
1330 except OSError:
1331 print("cannot open ", full_filename)
1332 raise Http404
1333 else:
1334 response = sendfile(request, full_filename)
1335 response["Accept-Ranges"] = "bytes"
1336 file_.close()
1337 return response
1340##########################################################################
1341#
1342# Views that update model objects (besides import/upload)
1343#
1344##########################################################################
1345class APILookupView(View):
1346 def get(self, request):
1347 title = request.GET["title"]
1348 year = request.GET.get("year", False)
1349 authors = request.GET.get("authors", False)
1350 params = {
1351 "qs": [
1352 {"name": "title", "value": title, "not": False, "first": "", "last": ""},
1353 ],
1354 }
1355 if year:
1356 params["qs"].append(
1357 {"name": "date", "value": None, "not": False, "first": year, "last": year},
1358 )
1359 if authors:
1360 params["qs"].append(
1361 {"name": "author", "value": authors, "not": False, "first": "", "last": ""},
1362 )
1363 cmd = solr_cmds.solrSearchCmd(params)
1364 results = cmd.do()
1365 if len(results.docs):
1366 data = {"pid": results.docs[0]["pid"]}
1367 else:
1368 data = {}
1369 return JsonResponse(data)
1372class APIFetchView(View):
1373 def get(self, request):
1374 data = ""
1375 pid = request.GET["pid"]
1376 if pid:
1377 article = get_object_or_404(models.Article, pid=pid)
1378 data = article.get_citation(with_formatting=True)
1379 return HttpResponse(data)
1382class ArticlesAPIView(View):
1383 def get(self, request):
1384 # values_list returns queryset in format [('id',) ('id',)]
1385 nested_article_pids = models.Article.objects.values_list("pid")
1386 # We flatten it to a normal list ['id', 'id']
1387 article_pids = list(chain.from_iterable(nested_article_pids))
1388 return JsonResponse(
1389 {"articles": {"ids": article_pids, "total": nested_article_pids.count()}}
1390 )
1393class ArticleDumpAPIView(View):
1394 def get(self, request, *args, **kwargs):
1395 pid = kwargs.get("pid", None)
1397 if "/" in pid: 1397 ↛ 1398line 1397 didn't jump to line 1398 because the condition on line 1397 was never true
1398 article = model_helpers.get_article_by_doi(pid, prefetch=True)
1399 else:
1400 article = model_helpers.get_article(pid, prefetch=True)
1401 if not article: 1401 ↛ 1402line 1401 didn't jump to line 1402 because the condition on line 1401 was never true
1402 raise Http404
1404 date_accepted = date_published = date_online_first = None
1405 if article.date_accepted: 1405 ↛ 1407line 1405 didn't jump to line 1407 because the condition on line 1405 was always true
1406 date_accepted = article.date_accepted.strftime("%Y-%m-%d")
1407 if article.date_online_first: 1407 ↛ 1409line 1407 didn't jump to line 1409 because the condition on line 1407 was always true
1408 date_online_first = article.date_online_first.strftime("%Y-%m-%d")
1409 if article.date_published: 1409 ↛ 1411line 1409 didn't jump to line 1411
1410 date_published = article.date_published.strftime("%Y-%m-%d")
1411 date_received = (
1412 article.date_received.strftime("%Y-%m-%d") if article.date_received else None
1413 )
1414 date_revised = article.date_revised.strftime("%Y-%m-%d") if article.date_revised else None
1416 author_names = models.get_names(article, "author")
1417 if author_names: 1417 ↛ 1420line 1417 didn't jump to line 1420 because the condition on line 1417 was always true
1418 authors = "; ".join(author_names)
1419 else:
1420 author_names = models.get_names(article, "editor")
1421 if author_names:
1422 authors = "; ".join(author_names) + " (" + str(_("éd.")) + ")"
1423 else:
1424 authors = None
1426 page_count = article.get_article_page_count()
1428 result = {
1429 "title_tex": article.title_tex,
1430 "title_html": article.title_html,
1431 "trans_title_tex": article.trans_title_tex,
1432 "trans_title_html": article.trans_title_html,
1433 "lang": article.lang,
1434 "doi": article.doi or None,
1435 "pid": article.pid,
1436 "authors": authors,
1437 "issue_pid": article.my_container.pid,
1438 "colid": article.my_container.my_collection.pid,
1439 "volume": article.my_container.volume,
1440 "number": article.my_container.number,
1441 "year": article.my_container.year,
1442 "issue_title": article.my_container.title_tex,
1443 "citation": article.get_citation(request),
1444 "date_accepted": date_accepted,
1445 "date_online_first": date_online_first,
1446 "date_published": date_published,
1447 "date_received": date_received,
1448 "date_revised": date_revised,
1449 "page_count": page_count,
1450 "body_html": article.body_html,
1451 "pci_section": article.get_pci_section(),
1452 }
1454 extids = []
1455 for extid in article.extid_set.all():
1456 extids.append([extid.id_type, extid.id_value])
1457 result["extids"] = extids
1459 result["kwds"] = [
1460 {"type": kwd.type, "lang": kwd.lang, "value": kwd.value}
1461 for kwd in article.kwd_set.all()
1462 ]
1464 awards = []
1465 for award in article.award_set.all():
1466 awards.append([award.abbrev, award.award_id])
1467 result["awards"] = awards
1469 abstracts = []
1470 for abstract in article.abstract_set.all():
1471 abstracts.append(
1472 {
1473 "lang": abstract.lang,
1474 "value_tex": abstract.value_tex,
1475 "value_html": abstract.value_html,
1476 }
1477 )
1478 result["abstracts"] = abstracts
1480 bibitems = []
1481 for bib in article.bibitem_set.all():
1482 bibitems.append(bib.citation_html)
1483 result["bibitems"] = bibitems
1485 return JsonResponse(result)
1488class BookDumpAPIView(View):
1489 def get(self, request, *args, **kwargs):
1490 pid = kwargs.get("pid", None)
1492 book = model_helpers.get_container(pid)
1493 if not book: 1493 ↛ 1494line 1493 didn't jump to line 1494 because the condition on line 1493 was never true
1494 raise Http404
1496 result = {
1497 "title_tex": book.title_tex,
1498 "title_html": book.title_html,
1499 "doi": book.doi or "",
1500 "citation": book.get_citation(request),
1501 }
1503 extids = []
1504 for extid in book.extid_set.all():
1505 extids.append([extid.id_type, extid.id_value])
1506 result["extids"] = extids
1508 result["kwds"] = [
1509 {"type": kwd.type, "lang": kwd.lang, "value": kwd.value} for kwd in book.kwd_set.all()
1510 ]
1512 abstracts = []
1513 for abstract in book.abstract_set.all():
1514 abstracts.append(
1515 {
1516 "lang": abstract.lang,
1517 "value_tex": abstract.value_tex,
1518 "value_html": abstract.value_html,
1519 }
1520 )
1521 result["abstracts"] = abstracts
1523 bibitems = []
1524 for bib in book.bibitem_set.all():
1525 bibitems.append(bib.citation_html)
1526 result["bibitems"] = bibitems
1528 return JsonResponse(result)
1531class AllIssuesAPIView(View):
1532 def get(self, request):
1533 issues_pids = list(models.Container.objects.values_list("pid", flat=True).order_by("pid"))
1534 return JsonResponse({"issues": issues_pids})
1537class CollectionIssnAPIView(View):
1538 def get(self, request, *args, **kwargs):
1539 collection = get_object_or_404(
1540 models.Collection,
1541 resourceid__id_value=kwargs.get("issn"),
1542 )
1543 if collection.parent:
1544 url = (
1545 ""
1546 if collection.parent.pid == collection.pid
1547 else collection.parent.get_absolute_url()
1548 )
1549 url += f"#{collection.pid}"
1550 return JsonResponse({"url": url})
1551 return JsonResponse({"url": collection.get_absolute_url()})
1554class CollectionsAPIView(View):
1555 def get(self, request):
1556 collections_pids = list(
1557 models.Collection.objects.filter(parent__isnull=True).values_list("pid", flat=True)
1558 )
1559 return JsonResponse({"collections": collections_pids})
1562class CollectionExportCSV(View):
1563 def get(self, request, *args, **kwargs):
1564 colid = kwargs.get("colid")
1565 collection = get_object_or_404(models.Collection, pid=colid)
1567 response = HttpResponse(content_type="text/csv")
1568 response["Content-Disposition"] = f'attachment; filename="{collection.pid}.csv"'
1570 csv_header = [
1571 "doi",
1572 "title",
1573 "date_accepted",
1574 "date_first_publication",
1575 ]
1577 writer = csv.writer(response, delimiter="\t")
1578 writer.writerow(csv_header)
1580 for article in models.Article.objects.filter(
1581 my_container__my_collection__pid=colid
1582 ).order_by("-date_accepted"):
1583 if article.date_published is not None or article.date_online_first is not None: 1583 ↛ 1580line 1583 didn't jump to line 1580 because the condition on line 1583 was always true
1584 first_online = (
1585 article.date_online_first
1586 if (article.date_online_first is not None and colid.lower()[0:2] == "cr")
1587 else article.date_published
1588 )
1589 writer.writerow(
1590 [
1591 f'=LIEN.HYPERTEXTE("https://doi.org/{article.doi}"; "{article.doi}")',
1592 xml_utils.normalise_span(article.title_tex),
1593 article.date_accepted.strftime("%Y-%m-%d")
1594 if article.date_accepted is not None
1595 else "",
1596 first_online.strftime("%Y-%m-%d"),
1597 ]
1598 )
1600 return response
1603class IssuesAPIView(View):
1604 def get(self, request, *args, **kwargs):
1605 colid = kwargs.get("colid", None)
1606 issues_pids = list(
1607 models.Container.objects.filter(
1608 Q(my_collection__pid=colid) | Q(my_collection__parent__pid=colid)
1609 )
1610 .values_list("pid", flat=True)
1611 .order_by("pid")
1612 )
1613 return JsonResponse({"issues": issues_pids})
1616class IssueListAPIView(View):
1617 def get(self, request, *args, **kwargs):
1618 pid = kwargs.get("pid", None)
1619 articles_pids = list(
1620 models.Article.objects.filter(my_container__pid=pid)
1621 .values_list("pid", flat=True)
1622 .order_by("pid")
1623 )
1624 return JsonResponse({"articles": articles_pids})
1627class ItemXMLView(View):
1628 def get(self, request, *args, **kwargs):
1629 pid = kwargs.get("pid", None)
1630 full_xml = self.request.GET.get("full_xml", "1")
1632 full_xml = False if full_xml == "0" or full_xml == "false" else True
1633 if pid.find(".") > 0:
1634 # The id given is a DOI
1635 article = model_helpers.get_article_by_doi(pid)
1636 if article: 1636 ↛ 1639line 1636 didn't jump to line 1639 because the condition on line 1636 was always true
1637 pid = article.pid
1638 else:
1639 raise Http404
1641 xml_body = ptf_cmds.exportPtfCmd(
1642 {
1643 "pid": pid,
1644 "with_internal_data": False,
1645 "with_binary_files": False,
1646 "for_archive": True,
1647 "full_xml": full_xml,
1648 }
1649 ).do()
1651 return HttpResponse(xml_body, content_type="application/xml")
1654class ItemFileListAPIView(View):
1655 def get(self, request, *args, **kwargs):
1656 pid = kwargs.get("pid", None)
1658 resource = model_helpers.get_resource(pid)
1659 if not resource: 1659 ↛ 1660line 1659 didn't jump to line 1660 because the condition on line 1659 was never true
1660 raise Http404
1662 obj = resource.cast()
1663 binary_files = obj.get_binary_files_location()
1665 result = {"files": binary_files}
1667 if obj.classname == "Container": 1667 ↛ 1668line 1667 didn't jump to line 1668 because the condition on line 1667 was never true
1668 articles = []
1669 for article in obj.article_set.all():
1670 article_files = article.get_binary_files_location()
1671 articles.append({"pid": article.pid, "files": article_files})
1672 result["articles"] = articles
1674 return JsonResponse(result)
1677class APIMatchOneView(View):
1678 def get(self, request, *args, **kwargs):
1679 pid = kwargs.get("pid", None)
1680 seq = kwargs.get("seq", 0)
1681 what = kwargs.get("what", "")
1683 resource = model_helpers.get_resource(pid)
1684 if not resource:
1685 raise Http404
1687 if what not in ["zbl-item-id", "mr-item-id", "doi", "numdam-id", "pmid"]:
1688 return HttpResponseBadRequest(
1689 "Bad request: 'what' has to be one"
1690 "of {zbl-item-id, mr-item-id, doi, numdam-id, pmid}"
1691 )
1692 result = ""
1694 status = 200
1695 message = ""
1697 try:
1698 obj = resource.cast()
1699 seq = int(seq)
1700 if seq == 0:
1701 # Article, Book or Book part match
1702 if obj.classname.lower() == "article":
1703 result = matching.match_article(obj, what)
1704 else:
1705 # TODO match book
1706 pass
1707 else:
1708 bibitem = model_helpers.get_bibitem_by_seq(obj, seq)
1709 if not bibitem:
1710 raise Http404
1711 result = matching.match_bibitem(bibitem, what)
1713 message = pid + " " + str(seq) + " " + what + " : " + result
1714 except Timeout as exception:
1715 return HttpResponse(exception, status=408)
1716 except Exception as exception:
1717 return HttpResponseServerError(exception)
1719 data = {"message": message, "status": status}
1720 return JsonResponse(data)
1723class APIMatchAllView(View):
1724 @staticmethod
1725 def get_existing_ids(pid, what, force):
1726 query_bibitemids = (
1727 models.BibItemId.objects.filter(
1728 bibitem__resource__pid__contains=pid,
1729 id_type__in=what,
1730 )
1731 .select_related(
1732 "bibitem",
1733 "bibitem__resource",
1734 )
1735 .only("bibitem__resource__pid", "bibitem__sequence", "id_type")
1736 )
1737 query_extids = models.ExtId.objects.filter(
1738 resource__pid__contains=pid,
1739 id_type__in=what,
1740 ).select_related("resource")
1741 if force == "1":
1742 query_bibitemids = query_bibitemids.exclude(checked=False, false_positive=False)
1743 query_extids = query_extids.exclude(checked=False, false_positive=False)
1744 if force == "2":
1745 query_bibitemids = models.BibItemId.objects.none()
1746 query_extids = models.ExtId.objects.none()
1747 bibitemids = {
1748 (bibitemid.bibitem.resource.pid, bibitemid.bibitem.sequence, bibitemid.id_type)
1749 for bibitemid in query_bibitemids
1750 }
1751 extids = {(extid.resource.pid, 0, extid.id_type) for extid in query_extids}
1752 return bibitemids.union(extids)
1754 @staticmethod
1755 def get_possible_ids(pid, what):
1756 bibitems = models.BibItem.objects.filter(resource__pid__contains=pid).select_related(
1757 "resource"
1758 )
1759 articles = models.Article.objects.filter(pid__contains=pid).exclude(
1760 classname="TranslatedArticle"
1761 )
1762 bibitemids = {
1763 (item.resource.pid, item.sequence, type_)
1764 for type_ in what
1765 for item in bibitems
1766 if type_ != "pmid"
1767 }
1768 # we remove doi from possible extids
1769 if "doi" in what:
1770 what.remove("doi")
1771 extids = {(article.pid, 0, type_) for type_ in what for article in articles}
1772 return bibitemids.union(extids)
1774 @staticmethod
1775 def delete_ids_if_necessary(pid, what, force):
1776 if force == "1":
1777 models.BibItemId.objects.filter(
1778 bibitem__resource__pid__contains=pid,
1779 id_type__in=what,
1780 checked=False,
1781 false_positive=False,
1782 ).delete()
1783 models.ExtId.objects.filter(
1784 resource__pid__contains=pid,
1785 id_type__in=what,
1786 checked=False,
1787 false_positive=False,
1788 ).delete()
1789 if force == "2":
1790 models.BibItemId.objects.filter(
1791 bibitem__resource__pid__contains=pid, id_type__in=what
1792 ).delete()
1793 models.ExtId.objects.filter(resource__pid__contains=pid, id_type__in=what).delete()
1795 def get(self, request, *args, **kwargs):
1796 pid = kwargs.get("pid", None)
1797 what = kwargs.get("what", "all")
1798 force = kwargs.get("force", "0")
1799 # if what contains several targets, they are separated with an underscore
1800 what = what.split("_")
1801 if force != "0":
1802 self.delete_ids_if_necessary(pid, what, force)
1803 existing = self.get_existing_ids(pid, what, force)
1804 possible = self.get_possible_ids(pid, what)
1805 ids = list(possible - existing)
1806 ids.sort(key=itemgetter(0, 1, 2))
1807 final = [f"{pid}/{number}/{what}" for pid, number, what in ids]
1808 return JsonResponse({"ids": final})
1811class UpdateMatchingView(View):
1812 resource = None
1814 def post_update(self):
1815 model_helpers.post_resource_updated(self.resource)
1817 def update_obj(self, action):
1818 if action == "delete":
1819 models.ExtId.objects.filter(resource=self.resource).delete()
1820 models.BibItemId.objects.filter(bibitem__resource=self.resource).delete()
1821 elif action == "mark-checked":
1822 models.ExtId.objects.filter(resource=self.resource).update(checked=True)
1823 models.BibItemId.objects.filter(bibitem__resource=self.resource).update(checked=True)
1824 elif action == "mark-unchecked":
1825 models.ExtId.objects.filter(resource=self.resource).update(checked=False)
1826 models.BibItemId.objects.filter(bibitem__resource=self.resource).update(checked=False)
1828 for bibitem in models.BibItem.objects.filter(resource=self.resource):
1829 cmd = xml_cmds.updateBibitemCitationXmlCmd()
1830 cmd.set_bibitem(bibitem)
1831 cmd.do()
1833 self.post_update()
1835 def get(self, request, *args, **kwargs):
1836 pid = kwargs.get("pid", None)
1837 action = kwargs.get("action", None)
1839 self.resource = model_helpers.get_resource(pid)
1840 if not self.resource:
1841 raise Http404
1843 self.update_obj(action)
1845 url = reverse("article", kwargs={"aid": self.resource.pid})
1846 return HttpResponseRedirect(url)
1849class UpdateExtIdView(View):
1850 obj = None
1851 resource = None
1852 parent = None
1854 def get_obj(self, pk):
1855 try:
1856 extid = models.ExtId.objects.get(pk=pk)
1857 except models.ExtId.DoesNotExist:
1858 raise Http404
1860 self.obj = extid
1861 self.resource = extid.resource
1863 def post_update(self):
1864 model_helpers.post_resource_updated(self.resource)
1866 def update_obj(self, action):
1867 if not self.obj:
1868 raise Http404
1870 if action == "delete":
1871 self.obj.delete()
1872 elif action == "toggle-checked":
1873 self.obj.checked = False if self.obj.checked else True
1874 self.obj.save()
1875 elif action == "toggle-false-positive":
1876 self.obj.false_positive = False if self.obj.false_positive else True
1877 self.obj.save()
1879 self.post_update()
1881 def get(self, request, *args, **kwargs):
1882 pk = kwargs.get("pk", None)
1883 action = kwargs.get("action", None)
1885 self.get_obj(pk)
1886 self.update_obj(action)
1888 if action == "delete":
1889 url = reverse("article", kwargs={"aid": self.resource.pid})
1890 return HttpResponseRedirect(url)
1891 return JsonResponse({})
1894class UpdateBibItemIdView(UpdateExtIdView):
1895 def get_obj(self, pk):
1896 try:
1897 bibitemid = models.BibItemId.objects.get(pk=pk)
1898 except models.BibItemId.DoesNotExist:
1899 raise Http404
1901 self.obj = bibitemid
1902 self.parent = bibitemid.bibitem
1903 self.resource = bibitemid.bibitem.resource
1905 def post_update(self):
1906 cmd = xml_cmds.updateBibitemCitationXmlCmd()
1907 cmd.set_bibitem(self.parent)
1908 cmd.do()
1910 model_helpers.post_resource_updated(self.resource)
1913class APIFetchId(View):
1914 def get(self, request, *args, **kwargs):
1915 id_ = kwargs.get("id", None)
1916 what = kwargs.get("what", None)
1917 pk = kwargs.get("pk")
1918 resource = kwargs["resource"]
1920 if what == "pmid":
1921 data = {"result": "", "status": 200}
1922 return JsonResponse(data)
1924 if not id_:
1925 return HttpResponseBadRequest("Bad request: 'id' is none")
1927 if what not in ["zbl-item-id", "mr-item-id", "doi", "numdam-id"]:
1928 return HttpResponseBadRequest(
1929 "Bad request: 'what' has to be one" "of {zbl-item-id, mr-item-id, doi, numdam-id}"
1930 )
1932 try:
1933 result = matching.fetch_id(id_, what)
1934 except (IndexError, RequestException) as exception:
1935 return HttpResponseServerError(exception)
1937 if resource == "bibitemid":
1938 bibitem = models.BibItem.objects.get(pk=pk)
1939 raw = bibitem.citation_html
1940 else:
1941 article = models.Article.objects.get(pk=pk)
1942 raw = article.get_citation(request)
1943 result = highlight_diff(raw, result)
1945 data = {"result": result, "status": 200}
1946 return JsonResponse(data)
1949class APIFetchAllView(View):
1950 def get(self, request, *args, **kwargs):
1951 pid = kwargs.get("pid", None)
1953 article = model_helpers.get_article(pid)
1954 if not article:
1955 raise Http404
1957 ids = matching.get_all_fetch_ids(article)
1959 data = {"ids": ids}
1960 return JsonResponse(data)
1963@require_http_methods(["GET"])
1964def malsm_books(request, pid="MALSM", api=False):
1965 template = "malsm.html"
1966 context = {
1967 "journal": model_helpers.get_collection(pid),
1968 "coltype": "book",
1969 "template": template,
1970 }
1972 return render(request, template, context)
1975class LatestArticlesFeed(Feed):
1976 link = ""
1977 ttl = 120
1979 def get_feed(self, obj, request):
1980 feed = super().get_feed(obj, request)
1981 feed.feed["language"] = get_language()
1982 return feed
1984 def get_object(self, request, name, jid=""):
1985 """
1986 Select the site whose RSS feed is requested.
1987 It is annotated with an optional `requested_col_id` for sites with multiple collections (ex: proceedings).
1988 """
1989 self.request = request
1990 return models.Site.objects.annotate(requested_col_id=Value(jid)).get(name=name)
1992 def title(self, obj):
1993 if obj.requested_col_id:
1994 collection = get_object_or_404(
1995 models.Collection, sites__name=obj.name, pid=obj.requested_col_id
1996 )
1997 else:
1998 collection = get_object_or_404(models.Collection, sites__name=obj.name)
1999 return collection.title_sort
2001 def description(self):
2002 return _("Flux RSS des derniers articles parus")
2004 def items(self, obj):
2005 qs = models.Article.objects.filter(sites=obj.pk)
2006 if obj.requested_col_id:
2007 qs = qs.filter(my_container__my_collection__pid=obj.requested_col_id)
2008 return qs.exclude(classname="TranslatedArticle").order_by(
2009 Greatest("date_online_first", "date_published").desc(nulls_last=True), "-seq"
2010 )[:20]
2012 def item_title(self, item):
2013 return f"{item.get_authors_short()} - {item.title_html}"
2015 def item_description(self, item):
2016 language = get_language()
2017 abstracts = item.get_abstracts()
2018 if not abstracts:
2019 return self.item_title(item)
2020 try:
2021 return abstracts.get(lang=language).value_html
2022 except models.Abstract.DoesNotExist:
2023 return abstracts.first().value_html
2025 def item_pubdate(self, item):
2026 if item.date_published:
2027 return item.date_published
2028 return item.date_online_first
2030 def item_link(self, item):
2031 if item.doi:
2032 return f"{settings.DOI_BASE_URL}{item.doi}"
2033 return item.get_absolute_url()
2036class LatestIssue(ItemView):
2037 def get(self, request, *args, **kwargs):
2038 pid = kwargs.get("pid")
2039 if pid is None: 2039 ↛ 2040line 2039 didn't jump to line 2040 because the condition on line 2039 was never true
2040 return HttpResponse(status=404)
2042 container_issues = (
2043 models.Container.objects.filter(my_collection__pid=pid)
2044 .all()
2045 .order_by("-vseries_int", "-year", "-volume_int", "-number_int")
2046 )
2047 if container_issues is None: 2047 ↛ 2048line 2047 didn't jump to line 2048 because the condition on line 2047 was never true
2048 return HttpResponse(status=404)
2050 container = container_issues.first()
2051 url = container.get_absolute_url()
2052 return HttpResponseRedirect(url)
2055class RSSTemplate(TemplateView):
2056 template_name = "blocks/syndication_feed.html"
2058 def dispatch(self, request, *args, **kwargs):
2059 # Numdam does not have an rss feed for now
2060 if settings.SITE_ID == 3:
2061 raise Http404
2062 return super().dispatch(request, *args, **kwargs)
2064 def get_context_data(self, **kwargs):
2065 context = super().get_context_data(**kwargs)
2067 context["journal"] = model_helpers.get_collection(settings.COLLECTION_PID)
2069 return context
2072class ArticleEditAPIView(View):
2073 def __init__(self, *args, **kwargs):
2074 super().__init__(*args, **kwargs)
2075 self.doi = None
2076 self.colid = None
2077 self.edit_all_fields = False
2078 self.fields_to_update = (
2079 []
2080 ) # Must be used to specify the fields to update (title, authors, references...)
2082 @method_decorator(csrf_exempt)
2083 def dispatch(self, request, *args, **kwargs):
2084 return super().dispatch(request, *args, **kwargs)
2086 def save_data(self, data_article):
2087 pass
2089 def restore_data(self, article):
2090 pass
2092 def convert_data_for_editor(self, data_article):
2093 data_article.trans_title_formulas = []
2094 data_article.title_formulas = []
2095 data_article.abstract_formulas = []
2096 data_article.trans_abstract_formulas = []
2098 data_article.title_tex, data_article.trans_title_tex = get_tex_from_xml(
2099 data_article.title_xml, "title", add_span_around_tex_formula=True
2100 )
2102 for contrib in data_article.contributors:
2103 contrib["address_text"] = "\n".join([address for address in contrib["addresses"]])
2105 if len(data_article.abstracts) == 0:
2106 data_article.abstracts = [
2107 {
2108 "tag": "abstract",
2109 "lang": data_article.lang,
2110 "value_html": "",
2111 "value_tex": "",
2112 "value_xml": "",
2113 }
2114 ]
2116 for abstract in data_article.abstracts:
2117 if abstract["value_xml"]:
2118 value_tex = get_tex_from_xml(
2119 abstract["value_xml"], "abstract", add_span_around_tex_formula=True
2120 )
2121 else:
2122 value_tex = ""
2123 abstract["value_tex"] = value_tex
2125 data_article.conference = ", ".join(
2126 [subj["value"] for subj in data_article.subjs if subj["type"] == "conference"]
2127 )
2128 data_article.topics = [
2129 subj["value"] for subj in data_article.subjs if subj["type"] == "topic"
2130 ]
2132 data_article.pci_section = "".join(
2133 [subj["value"] for subj in data_article.subjs if subj["type"] == "pci"]
2134 )
2136 data_article.subjs = [
2137 subj
2138 for subj in data_article.subjs
2139 if subj["type"] not in ["conference", "topic", "pci"]
2140 ]
2142 with_ordered_label = True
2143 for i, ref in enumerate(data_article.bibitems, start=1):
2144 model_data_converter.convert_refdata_for_editor(ref)
2145 if ref.label != f"[{str(i)}]":
2146 with_ordered_label = False
2147 data_article.bibitems_with_ordered_label = with_ordered_label
2149 ext_link = model_data.get_extlink(data_article, "icon")
2150 data_article.icon_url = None
2151 if ext_link:
2152 data_article.icon_url = resolver.get_icon_url(None, ext_link["location"])
2154 def convert_data_from_editor(self, data_article):
2155 xtitle = CkeditorParser(
2156 html_value=data_article.title_tex,
2157 mml_formulas=data_article.title_formulas,
2158 ignore_p=True,
2159 )
2160 data_article.title_html = xtitle.value_html
2161 data_article.title_tex = xtitle.value_tex
2163 trans_title_xml = ""
2164 if data_article.trans_title_tex:
2165 xtranstitle = CkeditorParser(
2166 html_value=data_article.trans_title_tex,
2167 mml_formulas=data_article.trans_title_formulas,
2168 ignore_p=True,
2169 )
2170 data_article.trans_title_html = xtranstitle.value_html
2171 data_article.trans_title_tex = xtranstitle.value_tex
2172 trans_title_xml = xtranstitle.value_xml
2174 data_article.title_xml = get_title_xml(
2175 xtitle.value_xml, trans_title_xml, data_article.trans_lang, with_tex_values=False
2176 )
2178 for contrib in data_article.contributors:
2179 contrib["addresses"] = []
2180 if "address_text" in contrib:
2181 contrib["addresses"] = contrib["address_text"].split("\n")
2183 for i, abstract in enumerate(data_article.abstracts):
2184 if i > 0:
2185 xabstract = CkeditorParser(
2186 html_value=abstract["value_tex"],
2187 mml_formulas=data_article.trans_abstract_formulas,
2188 )
2189 else:
2190 xabstract = CkeditorParser(
2191 html_value=abstract["value_tex"], mml_formulas=data_article.abstract_formulas
2192 )
2193 abstract["value_html"] = xabstract.value_html
2194 abstract["value_tex"] = xabstract.value_tex
2195 abstract["value_xml"] = jats_from_abstract(
2196 abstract["lang"], data_article.lang, xabstract
2197 )
2199 data_article.subjs = [
2200 subj for subj in data_article.subjs if subj["type"] not in ["conference", "topic"]
2201 ]
2202 if data_article.topics:
2203 data_article.subjs.extend(
2204 [{"type": "topic", "lang": "en", "value": topic} for topic in data_article.topics]
2205 )
2206 if data_article.conference:
2207 data_article.subjs.append(
2208 {"type": "conference", "lang": "en", "value": data_article.conference}
2209 )
2210 if data_article.pci_section:
2211 data_article.subjs.append(
2212 {"type": "pci", "lang": "en", "value": data_article.pci_section}
2213 )
2215 if self.edit_all_fields or "bibitems" in self.fields_to_update:
2216 for i, ref in enumerate(data_article.bibitems, start=1):
2217 if data_article.bibitems_with_ordered_label and ref["type"] != "unknown":
2218 ref["label"] = f"[{str(i)}]"
2219 if "doi" in ref and len(ref["doi"]) > 0:
2220 doi = xml_utils.clean_doi(ref["doi"])
2221 ref["doi"] = doi
2222 ref["extids"] = [["doi", doi]]
2223 else:
2224 ref["extids"] = []
2225 # URLs are in <comment>
2226 # if 'url' in ref and len(ref['url']) > 0:
2227 # ref['ext_links'] = [{'rel': '',
2228 # 'mimetype': '',
2229 # 'location': ref['url'],
2230 # 'base': '',
2231 # 'metadata': ''}]
2232 # else:
2233 # ref['ext_links'] = []
2235 author_array = ref["contribs_text"].split("\n")
2236 contribs = []
2237 for author_txt in author_array:
2238 if author_txt:
2239 lastname = firstname = ""
2240 pos = author_txt.find(", ")
2241 if pos > 0:
2242 lastname = author_txt[0:pos]
2243 firstname = author_txt[pos + 2 :]
2244 else:
2245 lastname = author_txt
2247 contrib = model_data.create_contributor()
2248 contrib["first_name"] = firstname
2249 contrib["last_name"] = lastname
2250 contrib["role"] = "author"
2251 contrib["contrib_xml"] = xml_utils.get_contrib_xml(contrib)
2252 contribs.append(contrib)
2253 ref["contributors"] = contribs
2255 def replace_p(self, obj, key):
2256 text = obj[key].replace("</p>", "")
2257 pos = text.find("<p>")
2258 if pos > -1:
2259 text = text[0:pos] + text[pos + 3 :]
2260 text = text.replace("<p>", "\n")
2261 obj[key] = text
2263 def replace_return(self, obj, key):
2264 text_array = obj[key].split("\n")
2265 text = "<br>".join([s for s in text_array])
2266 obj[key] = text
2268 def get(self, request, *args, **kwargs):
2269 doi = kwargs.get("doi", None)
2270 # colid = kwargs.get("colid", None)
2272 article = model_helpers.get_article_by_doi(doi, prefetch=True)
2273 if not article:
2274 raise Http404
2276 data_article = model_data_converter.db_to_article_data(article)
2277 self.convert_data_for_editor(data_article)
2279 def obj_to_dict(obj):
2280 return obj.__dict__
2282 dump = json.dumps(data_article, default=obj_to_dict)
2284 return HttpResponse(dump, content_type="application/json")
2286 def update_article(self, article_data, icon_file):
2287 self.save_data(article_data)
2289 collection = model_helpers.get_collection(self.colid)
2291 model_data_converter.update_data_for_jats(article_data)
2293 issue = None
2294 existing_article = model_helpers.get_article_by_doi(article_data.doi)
2295 new_data_article = None
2297 if existing_article is not None:
2298 issue = existing_article.my_container
2299 new_data_article = model_data_converter.db_to_article_data(existing_article)
2300 else:
2301 new_data_article = model_data.create_articledata()
2302 new_data_article.pid = article_data.pid
2303 new_data_article.doi = article_data.doi
2305 if self.edit_all_fields:
2306 new_data_article = article_data
2307 else:
2308 for field in self.fields_to_update:
2309 value = getattr(article_data, field)
2310 setattr(new_data_article, field, value)
2312 if self.edit_all_fields or "ext_links" in self.fields_to_update:
2313 # New icon
2314 if icon_file and issue:
2315 relative_file_name = resolver.copy_file_obj_to_article_folder(
2316 icon_file, self.colid, issue.pid, article_data.pid
2317 )
2318 ext_link = model_data.get_extlink(new_data_article, "icon")
2319 if ext_link is None:
2320 ext_link = model_data.create_extlink()
2321 ext_link["rel"] = "icon"
2322 ext_link["location"] = relative_file_name
2323 new_data_article.ext_links.append(ext_link)
2325 # No or removed icon
2326 if not icon_file and hasattr(article_data, "icon_url") and not article_data.icon_url:
2327 new_data_article.ext_links = [
2328 e for e in new_data_article.ext_links if e["rel"] != "icon"
2329 ]
2331 cmd = xml_cmds.addArticleXmlCmd(
2332 {"xarticle": new_data_article, "use_body": False, "issue": issue, "standalone": True}
2333 )
2334 cmd.set_collection(collection)
2335 article = cmd.do()
2337 self.restore_data(article)
2339 def post(self, request, *args, **kwargs):
2340 self.colid = kwargs.get("colid", None)
2341 self.doi = kwargs.get("doi", None)
2343 body_unicode = request.POST.get("data", "")
2344 body = json.loads(body_unicode)
2346 article_data = Munch(body)
2347 new_bibitems = []
2348 for bib in article_data.bibitems:
2349 new_bibitems.append(Munch(bib))
2350 article_data.bibitems = new_bibitems
2352 new_relations = []
2353 for relation in article_data.relations:
2354 new_relations.append(Munch(relation))
2355 article_data.relations = new_relations
2357 new_translations = []
2358 for translation in article_data.translations:
2359 new_translations.append(Munch(translation))
2360 article_data.translations = new_translations
2362 self.convert_data_from_editor(article_data)
2364 icon_file = None
2365 if "icon" in request.FILES:
2366 icon_file = request.FILES["icon"]
2368 self.update_article(article_data, icon_file)
2370 return JsonResponse({"message": "OK"})
2373class CollectionEditAPIView(View):
2374 def __init__(self, *args, **kwargs):
2375 super().__init__(*args, **kwargs)
2376 self.colid = None
2377 self.collection = None
2378 self.edit_all_fields = True
2379 self.fields_to_update = (
2380 []
2381 ) # Must be used to specify the fields to update (title, wall, periods...)
2383 @method_decorator(csrf_exempt)
2384 def dispatch(self, request, *args, **kwargs):
2385 return super().dispatch(request, *args, **kwargs)
2387 def save_data(self, data):
2388 pass
2390 def restore_data(self, collection):
2391 pass
2393 def convert_data_for_editor(self, data):
2394 data.title, data.trans_title = get_tex_from_xml(
2395 data.title_xml, "title", add_span_around_tex_formula=True
2396 )
2397 # TODO: move periods inside GDML
2398 data.periods = []
2400 def convert_data_from_editor(self, data):
2401 xtitle = CkeditorParser(html_value=data.title_tex, mml_formulas=[], ignore_p=True)
2402 data.title_html = xtitle.value_html
2403 data.title_tex = xtitle.value_tex
2405 # TODO: get_title_xml for Collections
2406 data.title_xml = get_title_xml(xtitle.value_xml, with_tex_values=False)
2408 ids = []
2409 if data.issn:
2410 ids.append(["issn", data.issn])
2411 if data.e_issn:
2412 ids.append(["e-issn", data.e_issn])
2413 data.ids = ids
2415 def replace_p(self, obj, key):
2416 text = obj[key].replace("</p>", "")
2417 pos = text.find("<p>")
2418 if pos > -1:
2419 text = text[0:pos] + text[pos + 3 :]
2420 text = text.replace("<p>", "\n")
2421 obj[key] = text
2423 def replace_return(self, obj, key):
2424 text_array = obj[key].split("\n")
2425 text = "<br>".join([s for s in text_array])
2426 obj[key] = text
2428 def get(self, request, *args, **kwargs):
2429 colid = kwargs.get("colid", None)
2431 self.collection = collection = model_helpers.get_collection(colid, sites=False)
2432 if not collection:
2433 raise Http404
2435 data = model_data_converter.db_to_publication_data(collection)
2436 self.convert_data_for_editor(data)
2438 def obj_to_dict(obj):
2439 return obj.__dict__
2441 dump = json.dumps(data, default=obj_to_dict)
2443 return HttpResponse(dump, content_type="application/json")
2445 def update_collection(self, data):
2446 self.save_data(data)
2448 existing_collection = model_helpers.get_collection(self.colid, sites=False)
2449 if existing_collection is None:
2450 cls = ptf_cmds.addCollectionPtfCmd
2451 new_data = data
2452 else:
2453 cls = ptf_cmds.updateCollectionPtfCmd
2455 if self.edit_all_fields:
2456 new_data = data
2457 else:
2458 new_data = model_data_converter.db_to_publication_data(existing_collection)
2460 for field in self.fields_to_update:
2461 value = getattr(data, field)
2462 setattr(new_data, field, value)
2464 params = {"xobj": new_data, "solr_commit": False}
2465 cmd = cls(params)
2466 if new_data.provider:
2467 provider = model_helpers.get_provider_by_name(new_data.provider)
2468 else:
2469 provider = model_helpers.get_provider("mathdoc-id")
2470 cmd.set_provider(provider)
2471 collection = cmd.do()
2473 # TODO: Move xml_cmds add_objects_with_location inside ptf_cmds
2474 # self.add_objects_with_location(xcol.ext_links, collection, "ExtLink")
2476 self.restore_data(collection)
2478 return collection
2480 def post(self, request, *args, **kwargs):
2481 self.colid = kwargs.get("colid", None)
2483 body_unicode = request.body.decode("utf-8")
2484 # POST.get('data') has to be used with a VueJS FormData
2485 # body_unicode = request.POST.get('data', '')
2486 body = json.loads(body_unicode)
2488 data = Munch(body)
2489 self.convert_data_from_editor(data)
2491 self.update_collection(data)
2493 return JsonResponse({"message": "OK"})
2496class ArticleCitedByView(View):
2497 def get(self, request, *args, **kwargs):
2498 doi = self.kwargs.get("doi", "").strip("/")
2500 if "/" in doi: 2500 ↛ 2503line 2500 didn't jump to line 2503 because the condition on line 2500 was always true
2501 resource = model_helpers.get_resource_by_doi(doi)
2502 else:
2503 resource = model_helpers.get_resource(doi)
2505 if not resource: 2505 ↛ 2506line 2505 didn't jump to line 2506 because the condition on line 2505 was never true
2506 raise Http404
2508 citations, sources, for_stats = citedby.get_citations(resource)
2509 if citations: 2509 ↛ 2510line 2509 didn't jump to line 2510 because the condition on line 2509 was never true
2510 status_code = 200
2511 else:
2512 status_code = 204
2514 data = {
2515 "result": {"citations": citations, "sources": sources, "doi": doi, "json": for_stats},
2516 "status": status_code,
2517 }
2518 return JsonResponse(data)
2521def export_citation(request, **kwargs):
2522 data = ""
2523 pid = kwargs.get("pid", "")
2524 ext = kwargs.get("ext", "")
2526 if "/" in pid: 2526 ↛ 2527line 2526 didn't jump to line 2527 because the condition on line 2526 was never true
2527 resource = model_helpers.get_resource_by_doi(pid)
2528 else:
2529 resource = model_helpers.get_resource(pid)
2531 if not resource: 2531 ↛ 2532line 2531 didn't jump to line 2532 because the condition on line 2531 was never true
2532 return HttpResponse(status=404)
2534 if hasattr(resource, "article"):
2535 document = resource.article
2536 else:
2537 document = model_helpers.get_container(resource.pid)
2539 filename = "citation." + ext
2540 if ext == "bib":
2541 data = document.get_bibtex(request)
2542 elif ext == "ris":
2543 data = document.get_ris(request)
2544 elif ext == "enw": 2544 ↛ 2547line 2544 didn't jump to line 2547 because the condition on line 2544 was always true
2545 data = document.get_endnote(request)
2547 if data: 2547 ↛ 2551line 2547 didn't jump to line 2551 because the condition on line 2547 was always true
2548 response = HttpResponse(data, content_type="text/html; charset=UTF-8")
2549 response["Content-Disposition"] = "attachment; filename=%s" % filename
2550 else:
2551 response = HttpResponse(status=204)
2552 return response
2555def get_tokenized(body):
2556 soup = BeautifulSoup(body, "html.parser")
2557 math_symbols = {}
2558 i = 0
2560 for tag in soup.select("span.mathjax-formula"):
2561 if tag.name == "span":
2562 math_symbols[i] = tag.decode_contents()
2563 tag.string = "[math_symbol_" + str(i) + "]"
2564 i += 1
2565 # else:
2566 # links[j] = tag.decode_contents()
2567 # tag.string = '$link_' + str(j) + '$'
2568 # j += 1
2570 body = str(soup)
2572 return {"body": body, "tokens": {"math": math_symbols}}
2575def detokenize(body, tokens):
2576 soup = BeautifulSoup(body, "html.parser")
2578 for tag in soup.select("span.mathjax-formula"):
2579 token_name = re.sub(r"\[|\]", "", tag.string)
2580 id_list = re.findall(r"\d+", token_name)
2581 if id_list:
2582 tag_id = int(id_list[0])
2583 if tag_id in tokens["math"]:
2584 tag.string = tokens["math"][tag_id]
2586 body = html.unescape(str(soup))
2588 return body
2591class ExportArticleHtml(View):
2592 def get(self, request, **kwargs):
2593 doi = kwargs.get("doi", None)
2594 if doi[-1] == "/": 2594 ↛ 2595line 2594 didn't jump to line 2595 because the condition on line 2594 was never true
2595 doi = doi[:-1]
2596 tokenize = kwargs.get("tokenize", None)
2597 a = Article.objects.get(doi=doi)
2598 body = a.body_html
2599 pid = a.__str__()
2601 if tokenize is not None: 2601 ↛ 2604line 2601 didn't jump to line 2604 because the condition on line 2601 was never true
2602 # tokenize = int(tokenize)
2604 tokens_infos = get_tokenized(body)
2605 body = tokens_infos["body"]
2606 # tokens = tokens_infos["tokens"]
2608 with NamedTemporaryFile("a+", encoding="utf-8") as html_article:
2609 html_article.write(body)
2610 html_article.seek(0)
2611 response = HttpResponse(html_article, content_type="text/html; charset=utf-8")
2612 response["Content-Disposition"] = "attachment; filename=" + pid + ".html"
2614 return response
2617class RecentArticlesPublished(View):
2618 # La méthode permet de retourner 3 articles récents au format JSON avec pour informations :
2619 # - titre de l'article
2620 # - le(s) auteur(s) de l'article
2621 # - collection de l'article
2622 # - doi
2623 # - Infos sur l'article
2624 # - url
2626 def get(self, request):
2627 articles = models.Article.objects.all().order_by(
2628 Greatest("date_online_first", "date_published").desc(nulls_last=True)
2629 )[:100]
2630 articles_infos = []
2631 container_pids = []
2632 nb_articles = articles.count()
2633 i = 0
2634 while i < nb_articles and len(articles_infos) < 3:
2635 article = articles[i]
2636 if article.my_container is not None and article.my_container.pid not in container_pids: 2636 ↛ 2648line 2636 didn't jump to line 2648 because the condition on line 2636 was always true
2637 container_pids.append(article.my_container.pid)
2638 if article.date_published: 2638 ↛ 2648line 2638 didn't jump to line 2648 because the condition on line 2638 was always true
2639 item = {
2640 "title": article.title_html,
2641 "authors": article.get_authors_short(),
2642 "collection": article.my_container.my_collection.title_tex,
2643 "doi": article.doi,
2644 "citation_source": article.get_citation_base(),
2645 "url": resolver.get_doi_url(article.doi),
2646 }
2647 articles_infos.append(item)
2648 i += 1
2649 return JsonResponse({"articles": articles_infos})
2652def get_first_n_authors(authors, n=3):
2653 if len(authors) > n:
2654 authors = authors[0:n]
2655 authors.append("...")
2656 return "; ".join(authors)
2659def get_suggested_articles(article):
2660 documents = []
2661 obj, created = models.RelatedArticles.objects.get_or_create(resource=article)
2662 solr_cmds.auto_suggest_doi(obj, article)
2664 if obj.doi_list: 2664 ↛ 2665line 2664 didn't jump to line 2665 because the condition on line 2664 was never true
2665 exclusion = obj.exclusion_list.split() if obj.exclusion_list else []
2666 # Filter the articles in the exclusion list and that have a DOI that includes the article DOI (translations)
2667 dois = [
2668 doi
2669 for doi in obj.doi_list.split()
2670 if doi not in exclusion and doi.find(article.doi) != 0
2671 ]
2672 for doi in dois:
2673 suggest = Article.objects.filter(doi=doi).first()
2674 doc = {}
2675 base_url = ""
2676 if suggest:
2677 doc = vars(suggest)
2678 doc["authors"] = models.get_names(suggest, "author")
2679 if suggest.my_container:
2680 collection = suggest.my_container.my_collection
2681 base_url = collection.website() or ""
2682 doc["year"] = suggest.my_container.year
2683 doc["journal_abbrev"] = collection.abbrev
2684 else:
2685 try:
2686 doc = crossref.crossref_request(doi)
2687 except:
2688 continue
2689 doc["title_html"] = doc.get("title", "")
2690 doc["journal_abbrev"] = doc.get("journal", "")
2691 authors = doc.get("authors", ";").split(";")
2692 doc["authors"] = [" ".join(au.split(",")).title() for au in authors]
2694 if doc:
2695 doc["authors"] = get_first_n_authors(doc.get("authors", []))
2696 if base_url and suggest:
2697 doc["url"] = base_url + "/articles/" + doi
2698 else:
2699 doc["url"] = resolver.get_doi_url(doi)
2700 documents.append(doc)
2701 return documents
2704@csrf_exempt
2705def update_suggest(request, doi):
2706 if request.method == "POST": 2706 ↛ exitline 2706 didn't return from function 'update_suggest' because the condition on line 2706 was always true
2707 article = Article.objects.filter(doi=doi).first()
2708 if not article:
2709 return JsonResponse({"message": "No article found"})
2710 obj, created = models.RelatedArticles.objects.get_or_create(resource=article)
2711 obj.resource_doi = article.doi
2712 obj.doi_list = request.POST.get("doi_list")
2713 obj.date_modified = request.POST.get("date_modified")
2714 obj.exclusion_list = request.POST.get("exclusion_list")
2715 obj.automatic_list = request.POST.get("automatic_list", True)
2716 obj.save()
2717 return JsonResponse({"message": "OK"})
2720@csrf_exempt
2721def graphical_abstract(request, doi):
2722 article = get_object_or_404(models.Article, doi=doi)
2723 if request.method == "POST": 2723 ↛ 2731line 2723 didn't jump to line 2731 because the condition on line 2723 was always true
2724 obj, created = models.GraphicalAbstract.objects.get_or_create(resource=article)
2725 obj.resource_doi = article.doi
2726 obj.date_modified = request.POST.get("date_modified")
2727 obj.graphical_abstract = request.FILES.get("graphical_abstract")
2728 obj.illustration = request.FILES.get("illustration")
2729 obj.save()
2730 return JsonResponse({"message": "OK"})
2731 elif request.method == "DELETE":
2732 obj = get_object_or_404(models.GraphicalAbstract, resource=article)
2733 if obj:
2734 obj.delete()
2735 return JsonResponse({"message": "OK"})
2738class MoreLikeThisView(View):
2739 def get(self, request, *args, **kwargs):
2740 doi = self.kwargs.get("doi", "")
2741 article = model_helpers.get_resource_by_doi(doi)
2742 if not article:
2743 raise Http404
2745 results = solr_cmds.research_more_like_this(article)
2746 return JsonResponse(results)
2749class HelpView(TemplateView):
2750 template_name = "help.html"
2752 def get_context_data(self, **kwargs):
2753 context = super().get_context_data(**kwargs)
2755 context["journal"] = model_helpers.get_collection(settings.COLLECTION_PID)
2757 return context
2760def home(request):
2761 context = {}
2762 return render(request, "help.html", context)