Coverage for apps/ptf/model_helpers.py: 75%

553 statements  

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

1import datetime 

2import itertools 

3import random 

4import re 

5import unicodedata 

6 

7import dateutil.parser 

8from unidecode import unidecode 

9 

10from django.conf import settings 

11from django.http import Http404 

12from django.shortcuts import get_list_or_404 

13from django.urls import reverse 

14from django.utils import timezone 

15from django.utils.translation import gettext_lazy as _ 

16 

17from .models import Article 

18from .models import Author 

19from .models import BibItem 

20from .models import BibItemId 

21from .models import Collection 

22from .models import Container 

23from .models import ExtId 

24from .models import ExtLink 

25from .models import Provider 

26from .models import PtfSite 

27from .models import Publisher 

28from .models import RelatedObject 

29from .models import RelationName 

30from .models import Relationship 

31from .models import Resource 

32from .models import SiteMembership 

33from .models import XmlBase 

34from .site_register import SITE_REGISTER 

35from .utils import volume_display 

36 

37########################################################################## 

38# 

39# Get functions 

40# 

41########################################################################## 

42 

43 

44def get_first_last_years(year): 

45 the_array = year.split("-") 

46 

47 fyear = the_array[0] 

48 lyear = the_array[1] if len(the_array) > 1 else fyear 

49 

50 return fyear, lyear 

51 

52 

53def get_provider_by_name(name): 

54 if name == "numdam": 

55 name = "mathdoc" 

56 

57 provider = Provider.objects.get(name=name) 

58 return provider 

59 

60 

61def get_provider(pid_type): 

62 provider = Provider.objects.get(pid_type=pid_type) 

63 return provider 

64 

65 

66def get_publisher(name): 

67 try: 

68 key = make_key(name) 

69 publisher = Publisher.objects.get(pub_key=key) 

70 except Publisher.DoesNotExist: 

71 publisher = None 

72 return publisher 

73 

74 

75def get_or_create_site(site_id, acro=None): 

76 try: 

77 site = PtfSite.objects.get(id=site_id) 

78 except PtfSite.DoesNotExist: 

79 site = PtfSite.objects.create(id=site_id, name=acro, domain=acro, acro=acro) 

80 return site 

81 

82 

83def get_journals(): 

84 journals = Collection.objects.filter(sites__id=settings.SITE_ID, coltype="journal") 

85 return journals.all() 

86 

87 

88def get_actas(): 

89 actas = Collection.objects.filter(sites__id=settings.SITE_ID, coltype="acta") 

90 return actas.all() 

91 

92 

93def get_lectures(): 

94 lectures = Collection.objects.filter(sites__id=settings.SITE_ID, coltype="lecture-notes") 

95 return lectures.all() 

96 

97 

98def get_proceedings(): 

99 proceedings = Collection.objects.filter(sites__id=settings.SITE_ID, coltype="proceeding") 

100 return proceedings.all() 

101 

102 

103def get_collection_of_books(with_lectures=False): 

104 filters = ["book-series"] 

105 if with_lectures: 

106 filters.append("lecture-notes") 

107 

108 book_series = Collection.objects.filter(sites__id=settings.SITE_ID, coltype__in=filters) 

109 return book_series.all() 

110 

111 

112def get_books(): 

113 return Container.objects.filter( 

114 sites__id=settings.SITE_ID, 

115 my_collection__coltype__in=["book-series", "lecture-notes", "book-edited-book"], 

116 ) 

117 

118 

119def get_theses(): 

120 theses = Container.objects.filter(sites__id=settings.SITE_ID, my_collection__coltype="thesis") 

121 return theses.all() 

122 

123 

124# TODO require the provider in the get_ functions as it serves as a namespace 

125 

126 

127def get_collection(pid, sites=True, prefetch=False): 

128 try: 

129 if sites: 

130 col = Collection.objects.get(pid=pid, sites__id=settings.SITE_ID) 

131 else: 

132 col = Collection.objects.get(pid=pid) 

133 except Collection.DoesNotExist: 

134 col = None 

135 return col 

136 

137 

138def get_volumes_in_collection(collection): 

139 """ 

140 used by issue-list.html and oai gallica 

141 return list of issues by volume if volume_int exist or by date 

142 return list of publishers GROUP BY name with date 

143 @param collection: 

144 @return: 

145 

146 """ 

147 

148 items = ( 

149 collection.content.filter(sites__id=settings.SITE_ID) 

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

151 .all() 

152 ) 

153 # We're building the following list: 

154 # vseries = [ { 'vseries_int': vseries_int, 

155 # 'volumes': list of volumes <=> volumes_in_vseriess 

156 # } 

157 # ] 

158 # 

159 # volumes_in_vseries = [ { 'volume_int': volume_int, 

160 # 'title': ? 

161 # 'fyear': fyear, 

162 # 'lyear': lyear, 

163 # 'issues': list of issues <=> issues_in_volume (list of issue objects) 

164 

165 if collection.collectionmembership_set.count() > 0: 165 ↛ 166line 165 didn't jump to line 166

166 col_membsership_qs = ( 

167 collection.collectionmembership_set.filter(container__sites__id=settings.SITE_ID) 

168 .order_by("-vseries_int", "-volume_int", "number_int") 

169 .all() 

170 ) 

171 joined = itertools.chain(items, col_membsership_qs) 

172 

173 def sorter(dict_): 

174 return ( 

175 dict_.vseries_int, 

176 dict_.year if hasattr(dict_, "year") else dict_.container.year, 

177 dict_.volume_int, 

178 dict_.number_int, 

179 ) 

180 

181 items = sorted(joined, key=sorter, reverse=True) 

182 

183 # items is now a collection of Container and/or CollectionMembership 

184 

185 issue_to_appear_pid = "" 

186 if hasattr(settings, "ISSUE_TO_APPEAR_PID"): 186 ↛ 187line 186 didn't jump to line 187, because the condition on line 186 was never true

187 issue_to_appear_pid = settings.ISSUE_TO_APPEAR_PID 

188 

189 issues_in_vseries = [] 

190 volumes_in_vseries = [] 

191 issues_in_volume = [] 

192 publishers = [] 

193 results_vseries = "-1" 

194 results_volume = "-1" 

195 results_year = "-1" 

196 results_fyear = results_lyear = -1 

197 volume_count = 0 

198 max_volume_count = 0 

199 max_width = 0 

200 width = 0 

201 for item in items: 

202 width += len(item.number) 

203 if hasattr(item, "container"): 203 ↛ 218line 203 didn't jump to line 218, because the condition on line 203 was never false

204 issue = item.container 

205 # The issue-list template use issue properties to display the list of issues 

206 # Replace the values with those of the CollectionMembership for the display 

207 issue.veries = item.vseries 

208 issue.volume = item.volume 

209 issue.number = item.number 

210 issue.vseries_int = item.vseries_int 

211 issue.volume_int = item.volume_int 

212 issue.number_int = item.number_int 

213 

214 year = issue.year 

215 vseries = item.vseries 

216 volume = item.volume 

217 else: 

218 issue = item 

219 year = issue.year 

220 vseries = issue.vseries 

221 volume = issue.volume 

222 

223 if issue.pid != issue_to_appear_pid: 223 ↛ 201line 223 didn't jump to line 201, because the condition on line 223 was never false

224 fyear, lyear = get_first_last_years(year) 

225 fyear = int(fyear) 

226 lyear = int(lyear) 

227 

228 # new volume found, we need to add issues_in_volume in the current volumes_in_veries 

229 if ( 229 ↛ 259line 229 didn't jump to line 259

230 results_volume != volume 

231 or (results_volume == "" and results_year != year) 

232 # or (results_volume == volume and results_year != year) 

233 ): 

234 # The first time, we simply add the issue in issues_in_volume (see below) 

235 # But we do not append in volumes_in_vseries 

236 if results_volume != "-1": 

237 volumes_in_vseries.append( 

238 { 

239 "volume": results_volume, 

240 "fyear": results_fyear, 

241 "lyear": results_lyear, 

242 "issues": issues_in_volume, 

243 } 

244 ) 

245 # Clear issues_in_volume to prepare a new volume 

246 issues_in_volume = [] 

247 

248 # Set the current volume info 

249 results_volume = volume 

250 results_year = year 

251 results_fyear = fyear 

252 results_lyear = lyear 

253 volume_count += 1 

254 if width > max_width: 

255 max_width = width 

256 width = 0 

257 

258 # New vseries found, we need to add volumes_in_vseries to the current vseries 

259 if results_vseries != vseries: 

260 # The first time, we do not append in issues_in_vseries. 

261 # We simply set the vseries info and add the issues in issues_in_volume below 

262 if results_vseries != "-1": 262 ↛ 263line 262 didn't jump to line 263, because the condition on line 262 was never true

263 issues_in_vseries.append( 

264 {"vseries": results_vseries, "volumes": volumes_in_vseries} 

265 ) 

266 volumes_in_vseries = [] 

267 results_vseries = vseries 

268 max_volume_count = max(0, volume_count) 

269 volume_count = 0 

270 

271 issues_in_volume.append(issue) 

272 

273 # we have to create a list of publishers with date - for Gallica OAI 

274 if issue.my_publisher: 

275 if publishers: 

276 item = publishers[-1] 

277 if issue.my_publisher.pub_name != item["name"]: 277 ↛ 278line 277 didn't jump to line 278

278 item = { 

279 "name": issue.my_publisher.pub_name, 

280 "fyear": fyear, 

281 "lyear": lyear, 

282 } 

283 publishers.append(item) 

284 else: 

285 if fyear < item["fyear"]: 285 ↛ 287line 285 didn't jump to line 287, because the condition on line 285 was never false

286 item["fyear"] = fyear 

287 if lyear > item["lyear"]: 287 ↛ 288line 287 didn't jump to line 288, because the condition on line 287 was never true

288 item["lyear"] = lyear 

289 else: 

290 publishers.insert( 

291 0, {"name": issue.my_publisher.pub_name, "fyear": fyear, "lyear": lyear} 

292 ) 

293 

294 # At the end of the loop, we need to append the remaining issues_in_volume 

295 # then volumes_in_vseries 

296 if results_volume != "-1" and issues_in_volume: 

297 volumes_in_vseries.append( 

298 { 

299 "volume": results_volume, 

300 "fyear": results_fyear, 

301 "lyear": results_lyear, 

302 "issues": issues_in_volume, 

303 } 

304 ) 

305 # volume_count += 1 

306 

307 if results_vseries != "-1" and volumes_in_vseries: 

308 issues_in_vseries.append({"vseries": results_vseries, "volumes": volumes_in_vseries}) 

309 max_volume_count = max(0, volume_count) 

310 

311 context = { 

312 "sorted_issues": issues_in_vseries, 

313 "volume_count": max_volume_count, 

314 "publishers": publishers, 

315 "max_width": max_width, 

316 } 

317 

318 return context 

319 

320 

321def get_container(pid, prefetch=False, sites=True): 

322 try: 

323 if prefetch: 

324 container = ( 

325 Container.objects.filter(sites__id=settings.SITE_ID, pid=pid) 

326 .prefetch_for_toc() 

327 .first() 

328 ) 

329 else: 

330 if sites: 330 ↛ 333line 330 didn't jump to line 333, because the condition on line 330 was never false

331 container = Container.objects.get(sites__id=settings.SITE_ID, pid=pid) 

332 else: 

333 container = Container.objects.get(pid=pid) 

334 

335 except Container.DoesNotExist: 

336 container = None 

337 return container 

338 

339 

340def get_book_serie(pid): 

341 try: 

342 book_serie = Collection.objects.get( 

343 pid=pid, coltype="book-series", sites__id=settings.SITE_ID 

344 ) 

345 except Collection.DoesNotExist: 

346 book_serie = None 

347 return book_serie 

348 

349 

350def get_issues_count_in_collection(pid): 

351 try: 

352 collection = Collection.objects.get(pid=pid) 

353 issues = Container.objects.filter(my_collection=collection).count() 

354 return issues 

355 except Exception: 

356 pass 

357 

358 

359def get_issues_in_volume(vid, is_cr=False, general_articles=False): 

360 # 08/09/2022: vid is no longer built (see get_vid in models.py) 

361 # It is now the pid of the first issue of the volume 

362 first_issue = get_container(vid) 

363 if first_issue is None: 

364 raise Http404 

365 

366 collection = first_issue.get_collection() 

367 year = first_issue.year 

368 vseries = first_issue.vseries 

369 volume = first_issue.volume 

370 

371 if is_cr: 

372 year_int = int(year) 

373 if year_int > 2020 and collection.pid not in ["CRMATH", "CRBIOL"]: 373 ↛ 376line 373 didn't jump to line 376, because the condition on line 373 was never true

374 # CRAS: Les thématiques à partir de 2021 ont un number en "T1", "T2",... 

375 # On trie par number pour avoir les thématiques isolés des autres 

376 queryset = Container.objects.order_by("number") 

377 elif year_int > 2022 and collection.pid == "CRBIOL": 377 ↛ 378line 377 didn't jump to line 378, because the condition on line 377 was never true

378 queryset = Container.objects.order_by("number") 

379 else: 

380 queryset = Container.objects.order_by("number_int") 

381 if general_articles: 381 ↛ 382line 381 didn't jump to line 382, because the condition on line 381 was never true

382 queryset = queryset.filter(title_html="") 

383 else: 

384 queryset = Container.objects.order_by("number_int") 

385 queryset = queryset.filter( 

386 my_collection__pid=collection.pid, year=year, vseries=vseries, volume=volume 

387 ).prefetch_for_toc() 

388 issues = get_list_or_404(queryset) 

389 

390 if is_cr and ( 390 ↛ 394line 390 didn't jump to line 394, because the condition on line 390 was never true

391 (year_int > 2020 and collection.pid != "CRBIOL") 

392 or (year_int > 2022 and collection.pid == "CRBIOL") 

393 ): 

394 issues_articles = [] 

395 grouped_articles = [] 

396 grouped_issue_articles = {"issue": None, "articles": grouped_articles} 

397 for issue in issues: 

398 if len(issue.title_html) == 0: 

399 grouped_articles.extend(issue.article_set.all().order_by_sequence()) 

400 grouped_issue_articles["issue"] = issue 

401 else: 

402 issues_articles.append( 

403 { 

404 "issue": issue, 

405 "articles": issue.article_set.all().order_by_published_date() 

406 if settings.SORT_ARTICLES_BY_DATE 

407 else issue.article_set.all().order_by_sequence(), 

408 } 

409 ) 

410 if grouped_issue_articles["issue"] is not None: 

411 issues_articles.insert(0, grouped_issue_articles) 

412 else: 

413 issues_articles = [ 

414 { 

415 "issue": issue, 

416 "articles": issue.article_set.all().order_by_published_date() 

417 if settings.SORT_ARTICLES_BY_DATE 

418 else issue.article_set.all().order_by_sequence(), 

419 } 

420 for issue in issues 

421 ] 

422 

423 return issues_articles, collection 

424 

425 

426def get_article(pid: str, prefetch=False, sites=True) -> Article | None: 

427 try: 

428 if prefetch: 

429 article = ( 

430 Article.objects.filter(sites__id=settings.SITE_ID, pid=pid).prefetch_all().first() 

431 ) 

432 else: 

433 if sites: 433 ↛ 436line 433 didn't jump to line 436, because the condition on line 433 was never false

434 article = Article.objects.get(sites__id=settings.SITE_ID, pid=pid) 

435 else: 

436 article = Article.objects.get(pid=pid) 

437 

438 except Article.DoesNotExist: 

439 article = None 

440 return article 

441 

442 

443def get_article_by_doi(doi, prefetch=False): 

444 try: 

445 if prefetch: 

446 article = ( 

447 Article.objects.filter(sites__id=settings.SITE_ID, doi=doi).prefetch_all().first() 

448 ) 

449 else: 

450 article = Article.objects.get(sites__id=settings.SITE_ID, doi=doi) 

451 

452 except Article.DoesNotExist: 

453 article = None 

454 return article 

455 

456 

457def get_articles(): 

458 article_qs = Article.objects.filter(sites__id=settings.SITE_ID).exclude( 

459 classname="TranslatedArticle" 

460 ) 

461 return article_qs 

462 

463 

464def get_articles_by_deployed_date(): 

465 sitemembership_qs = SiteMembership.objects.filter( 

466 site__id=settings.SITE_ID, resource__classname="Article" 

467 ).order_by("-deployed", "-seq") 

468 

469 articles = [sm.resource.cast() for sm in sitemembership_qs] 

470 return articles 

471 

472 

473def get_articles_by_published_date(): 

474 article_qs = Article.objects.filter(sites__id=settings.SITE_ID).exclude( 

475 classname="TranslatedArticle" 

476 ) 

477 if hasattr(settings, "ISSUE_TO_APPEAR_PID"): 

478 article_qs = article_qs.exclude(my_container__pid=settings.ISSUE_TO_APPEAR_PID) 

479 article_qs = article_qs.order_by("-date_published", "-seq") 

480 

481 return article_qs 

482 

483 

484def get_xmlbase(base): 

485 try: 

486 xmlbase = XmlBase.objects.get(base=base) 

487 except XmlBase.DoesNotExist: 

488 xmlbase = None 

489 return xmlbase 

490 

491 

492# RelationName are created with a fixture (see app/ptf/apps/ptf/fixtures/initial_data.json 

493# Example { "left" : "follows", "right" : "followed-by" } 

494# A related-article of an article has 1 relation name (ex "follows" or "followed-by") 

495# You need to know if the relation was stored in the left or right attribute of a RelationName, 

496# so that you can create/search the Relationship with the correct object/subject. 

497# Ex: with A "follows" B, A is the subject and B the object because "follows" is a RelationName.left attribute 

498# with A "followed-by" B, A is the object the B the subject because 

499# "followed-by" is a RelationName.right attribute 

500def get_relationname_left(left_name): 

501 try: 

502 relationname = RelationName.objects.get(left=left_name) 

503 except RelationName.DoesNotExist: 

504 relationname = None 

505 

506 return relationname 

507 

508 

509def get_relationname_right(right_name): 

510 try: 

511 relationname = RelationName.objects.get(right=right_name) 

512 except RelationName.DoesNotExist: 

513 relationname = None 

514 

515 return relationname 

516 

517 

518# See comments about RelationName above 

519def get_relationship(subject_pid, object_pid, relationname): 

520 try: 

521 relationship = Relationship.objects.get( 

522 subject_pid=subject_pid, object_pid=object_pid, rel_info=relationname 

523 ) 

524 except Relationship.DoesNotExist: 

525 relationship = None 

526 

527 return relationship 

528 

529 

530def get_extid(resource, id_type): 

531 extid = None 

532 extids = ExtId.objects.filter(resource=resource, id_type=id_type) 

533 if extids.count() > 0: 

534 extid = extids.first() 

535 

536 return extid 

537 

538 

539def get_bibitemid(bibitem, id_type): 

540 bibitemid = None 

541 bibitemids = BibItemId.objects.filter(bibitem=bibitem, id_type=id_type) 

542 if bibitemids.count() > 0: 

543 bibitemid = bibitemids.first() 

544 

545 return bibitemid 

546 

547 

548def get_bibitem_by_seq(article, seq): 

549 try: 

550 bibitem = article.bibitem_set.get(sequence=seq) 

551 except BibItem.DoesNotExist: 

552 bibitem = None 

553 return bibitem 

554 

555 

556def get_extlink(**filters): 

557 try: 

558 extlink = ExtLink.objects.get(**filters) 

559 except ExtLink.DoesNotExist: 

560 extlink = None 

561 return extlink 

562 

563 

564def get_related_object(**filters): 

565 """ 

566 Return RelatedObject with filters pass by params (all are optionals) 

567 resource, base, rel, mimetype, location, metadata, seq 

568 Check models.py for the params of a RelatedObject 

569 """ 

570 try: 

571 related_object = RelatedObject.objects.get(**filters) 

572 related_object.select_related() 

573 except RelatedObject.DoesNotExist: 

574 related_object = None 

575 return related_object 

576 

577 

578def get_authors_by_letter(first_letter): 

579 try: 

580 authors = Author.objects.filter(first_letter=first_letter).order_by("name") 

581 except Author.DoesNotExist: 

582 authors = None 

583 return authors 

584 

585 

586def make_key(string): 

587 n_string = unicodedata.normalize("NFKD", string).encode("ascii", "ignore").decode("ascii") 

588 n_string = re.sub(r"[^\w\s-]", "", n_string).strip().lower() 

589 n_string = re.sub(r"[-\s]+", "-", n_string) 

590 if len(n_string) > 64: 

591 n_string = n_string[:64] 

592 

593 return n_string if n_string else unidecode(string) 

594 

595 

596def get_resource(pid: str, prefetch=False) -> Resource | None: 

597 try: 

598 if prefetch: 

599 resource = ( 

600 Resource.objects.filter(sites__id=settings.SITE_ID, pid=pid).prefetch_all().first() 

601 ) 

602 else: 

603 resource = Resource.objects.get(pid=pid, sites__id=settings.SITE_ID) 

604 except Resource.DoesNotExist: 

605 resource = None 

606 return resource 

607 

608 

609def get_resource_by_doi(doi, prefetch=False): 

610 try: 

611 if prefetch: 611 ↛ 612line 611 didn't jump to line 612

612 resource = ( 

613 Resource.objects.filter(sites__id=settings.SITE_ID, doi=doi).prefetch_all().first() 

614 ) 

615 else: 

616 resource = Resource.objects.get(sites__id=settings.SITE_ID, doi=doi) 

617 

618 except Resource.DoesNotExist: 

619 resource = None 

620 return resource 

621 

622 

623def get_random_containers(): 

624 # TODO get the newest containers only 

625 

626 containers = Container.objects.all() 

627 random_list = random.sample(containers, 6) 

628 

629 return random_list 

630 

631 

632def parse_date_str(date_str): 

633 """ 

634 @param date_str:a string representing a date. Ex: "2017-01-10T18:24:58.202+01:00", "2017-05-03" 

635 @return:a localized datetime object (localized means that the date has a timezone) 

636 """ 

637 the_date = dateutil.parser.parse(date_str) 

638 if not timezone.is_aware(the_date): 

639 the_date = timezone.make_aware(the_date, datetime.UTC) 

640 return the_date 

641 

642 

643def get_issue_to_appear(colid): 

644 """ 

645 Some journals want to display "articles to appear" with articles that have been accepted but are not yet 

646 finalized (pages start at 1). ex: AIF 

647 :param pid: 

648 :return: The container object of articles to appear 

649 """ 

650 pid = "" 

651 

652 if hasattr(settings, "ISSUE_TO_APPEAR_PIDS"): 

653 if colid in settings.ISSUE_TO_APPEAR_PIDS: 

654 pid = settings.ISSUE_TO_APPEAR_PIDS[colid] 

655 

656 container = get_container(pid=pid, prefetch=True) 

657 return container 

658 

659 

660def get_number_from_doi(doi): 

661 value = 0 

662 

663 try: 

664 index = doi.rfind(".") 

665 index += 1 

666 if index > 0: 

667 str_value = doi[index:] 

668 value = int(str_value) 

669 except BaseException: 

670 pass 

671 

672 return value 

673 

674 

675def get_site_mersenne(collection_pid): 

676 key = collection_pid.lower() 

677 

678 # TODO refactor smai-jcm, centre_mersenne to have collection_pid == key 

679 # Do not use a find here, we want an access time in O(1) 

680 if key == "smai-jcm": 680 ↛ 681line 680 didn't jump to line 681, because the condition on line 680 was never true

681 key = "smai" 

682 elif key == "mersenne": 682 ↛ 683line 682 didn't jump to line 683, because the condition on line 682 was never true

683 key = "centre_mersenne" 

684 elif key == "malsm": 684 ↛ 685line 684 didn't jump to line 685, because the condition on line 684 was never true

685 key = "mbk" 

686 

687 try: 

688 site_item = settings.SITE_REGISTER[key] 

689 except KeyError: 

690 return None 

691 site_id = site_item["site_id"] 

692 site_acro = key 

693 

694 return get_or_create_site(site_id, site_acro) 

695 

696 

697def get_container_breadcrumb(collection, container, vseries, volume, number, stop_at_volume=False): 

698 crumbs = [] 

699 

700 if collection: 700 ↛ 714line 700 didn't jump to line 714, because the condition on line 700 was never false

701 collection = collection.get_top_collection() 

702 href = collection.get_absolute_url() 

703 # We show the collection title instead of the basic 'Feuilleter' for the sites with several collections: 

704 # 'ALL' and 'MBK' (proceedings site) 

705 if settings.COLLECTION_PID in ["ALL", "MBK"] or settings.SITE_NAME == "gdml": 705 ↛ 706line 705 didn't jump to line 706, because the condition on line 705 was never true

706 title = collection.title_html 

707 elif hasattr(settings, "SITE_NAME") and settings.SITE_NAME[0:2] == "cr": 

708 title = _("Consulter") 

709 else: 

710 title = _("Feuilleter") 

711 

712 crumbs.append({"href": href, "title": title}) 

713 

714 if container and container.ctype == "issue": 

715 # TODO : Here, we should have YEAR_BREACRUMB in all settings file 

716 # Until then we just search if YEAR_BREADCRUMB exists 

717 if hasattr(settings, "YEAR_BREADCRUMB"): 717 ↛ 718line 717 didn't jump to line 718, because the condition on line 717 was never true

718 title = str(container.year) 

719 href = reverse("articles-year", kwargs={"year": container.year}) 

720 else: 

721 href = reverse("volume-items", kwargs={"vid": container.get_vid()}) 

722 title = "{} {} : ".format(_("Série"), vseries) if vseries else "" 

723 if volume: 723 ↛ 724line 723 didn't jump to line 724, because the condition on line 723 was never true

724 title += f"{volume_display()} {volume} ({container.year})" 

725 else: 

726 title += "{} {}".format(_("Année"), container.year) 

727 crumbs.append({"href": href, "title": title}) 

728 

729 if not stop_at_volume: 

730 if number: 730 ↛ 752line 730 didn't jump to line 752, because the condition on line 730 was never false

731 try: 

732 year = int(container.year) 

733 except ValueError: 

734 year = 0 

735 

736 if ( 736 ↛ 745line 736 didn't jump to line 745

737 hasattr(settings, "SITE_NAME") 

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

739 and ( 

740 (settings.SITE_NAME != "crbiol" and year > 2020) 

741 or (settings.SITE_NAME == "crbiol" and year > 2022) 

742 ) 

743 and container.title_html == "" 

744 ): 

745 title = _("Articles du volume en cours") 

746 href = reverse("volume-general-items", kwargs={"vid": container.get_vid()}) 

747 else: 

748 href = container.get_absolute_url() 

749 title = "no." + " " + number 

750 crumbs.append({"href": href, "title": title}) 

751 

752 return crumbs 

753 

754 

755def get_breadcrumb(article, container, collection, stop_at_volume=False): 

756 all_crumbs = [] 

757 is_a_mersenne_books = False 

758 previous = next = "" 

759 article_crumb = None 

760 

761 if article: 

762 display_page = True 

763 if hasattr(settings, "PAGE_BREADCRUMB"): 763 ↛ 764line 763 didn't jump to line 764, because the condition on line 763 was never true

764 display_page = settings.PAGE_BREADCRUMB 

765 if display_page: 765 ↛ 769line 765 didn't jump to line 769, because the condition on line 765 was never false

766 href = article.get_absolute_url() 

767 title = article.get_breadcrumb_page_text() 

768 article_crumb = {"href": href, "title": title} 

769 previous = article.previous().get_absolute_url() if article.previous() else None 

770 next = article.next().get_absolute_url() if article.next() else None 

771 elif collection.pid == "CJPS" or settings.CONTAINER_SEQUENCED: 771 ↛ 772line 771 didn't jump to line 772, because the condition on line 771 was never true

772 previous = container.previous().get_absolute_url() if container.previous() else None 

773 next = container.next().get_absolute_url() if container.next() else None 

774 

775 crumbs = get_container_breadcrumb( 

776 collection, 

777 container, 

778 container.vseries, 

779 container.volume, 

780 container.number, 

781 stop_at_volume, 

782 ) 

783 if article_crumb: 

784 crumbs.append(article_crumb) 

785 

786 all_crumbs.append({"crumbs": crumbs, "previous": previous, "next": next}) 

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

788 is_a_mersenne_books = True 

789 else: 

790 is_a_mersenne_books = False 

791 # mersenne_books_crumb = {'href': collection.get_absolute_url(), 'title': collection.title_html} 

792 for collection_membership in container.collectionmembership_set.all(): 792 ↛ 793line 792 didn't jump to line 793, because the loop on line 792 never started

793 collection = collection_membership.collection 

794 vseries = collection_membership.vseries 

795 volume = collection_membership.volume 

796 number = collection_membership.number 

797 crumbs = get_container_breadcrumb( 

798 collection, container, vseries, volume, number, stop_at_volume=True 

799 ) 

800 if article_crumb: 

801 crumbs.append(article_crumb) 

802 

803 # uniquement pour les livres Mersenne ; si un livre appartient à une 2ème collection, on n'affiche que celle-ci 

804 # {{2eme collection}} > Titre Container > Tome1... 

805 if is_a_mersenne_books: 

806 all_crumbs.pop() 

807 # crumbs.insert(0, mersenne_books_crumb) 

808 all_crumbs.append({"crumbs": crumbs, "previous": previous, "next": next}) 

809 

810 show_tex = False 

811 if hasattr(settings, "SHOW_TEX"): 811 ↛ 814line 811 didn't jump to line 814, because the condition on line 811 was never false

812 show_tex = settings.SHOW_TEX 

813 

814 return {"all_crumbs": all_crumbs, "show_tex": show_tex} 

815 

816 

817########################################################################## 

818# 

819# Update functions 

820# 

821########################################################################## 

822def post_resource_updated(resource): 

823 """ 

824 A resource is modified (ex: matching), the last_modified date of its container has to be updated. 

825 :param resource: 

826 :return: 

827 """ 

828 obj = resource.cast() 

829 container = obj.get_container() 

830 if container: 830 ↛ exitline 830 didn't return from function 'post_resource_updated', because the condition on line 830 was never false

831 container.last_modified = timezone.now() 

832 container.save() 

833 

834 

835def update_deployed_date(resource, site, deployed_date_in_prod_to_restore=None, file_=None): 

836 """ 

837 Used by ptf_tools during DeployJatsIssue 

838 

839 Update the SiteMembership deployed_date of container/site based on the production website. 

840 - If there is no deployed_date in ptf_tools (not yet in prod), we create one. 

841 - with deployed_date_in_prod if it's not None (case when we restore data), or 

842 - with now if deployed_date_in_prod is None (first deploy in prod) 

843 - If the last_modified date of the container in ptf_tools is > deployed_date_in_prod 

844 (we have a new version in ptf_tools), we update deployed_date_in_prod with now(), 

845 - else we update deployed_date with deployed_date_in_prod 

846 (Normally, they should be equal) 

847 

848 :param resource: 

849 :param site: 

850 :param deployed_date_in_prod_to_restore: 

851 :param file_ file object to log info 

852 :return: 

853 """ 

854 

855 def get_deployed_date_in_prod(resource_, site_): 

856 deployed_date_in_prod = None 

857 try: 

858 membership = SiteMembership.objects.get(resource=resource_, site=site_) 

859 deployed_date_in_prod = membership.deployed 

860 except SiteMembership.DoesNotExist: 

861 pass 

862 

863 return deployed_date_in_prod 

864 

865 def update_or_create(resource_, site_, deployed): 

866 try: 

867 membership = SiteMembership.objects.get(resource=resource_, site=site_) 

868 membership.deployed = deployed 

869 except SiteMembership.DoesNotExist: 

870 membership = SiteMembership(resource=resource_, site=site_, deployed=deployed) 

871 membership.save() 

872 

873 container = article = None 

874 

875 if resource.classname == "Article": 875 ↛ 876line 875 didn't jump to line 876, because the condition on line 875 was never true

876 article = resource 

877 container = article.my_container 

878 else: 

879 container = resource 

880 

881 existing_deployed_date = get_deployed_date_in_prod(container, site) 

882 

883 # If we restore deployed_date, force the new value to the restored value 

884 if deployed_date_in_prod_to_restore: 

885 new_deployed_date = deployed_date_in_prod_to_restore 

886 else: 

887 # Get the existing deployed_date_in_prod (from SiteMembership) 

888 new_deployed_date = existing_deployed_date 

889 

890 # If there is no value of if the current version (last_modified) is newer, update the date 

891 if new_deployed_date is None or container.last_modified > new_deployed_date: 891 ↛ 896line 891 didn't jump to line 896, because the condition on line 891 was never false

892 new_deployed_date = timezone.now() 

893 

894 # Set the new value to the entire container/articles (+ collection) 

895 

896 if file_: 896 ↛ 897line 896 didn't jump to line 897, because the condition on line 896 was never true

897 file_.write( 

898 "{}. Date to restore: {}. Container.last_modified: {}, Existing date {}. New date {}\n".format( 

899 container.pid, 

900 deployed_date_in_prod_to_restore, 

901 container.last_modified, 

902 existing_deployed_date, 

903 new_deployed_date, 

904 ) 

905 ) 

906 

907 update_or_create(container, site, new_deployed_date) 

908 update_or_create(container.get_collection(), site, new_deployed_date) 

909 

910 if article is not None: 910 ↛ 911line 910 didn't jump to line 911, because the condition on line 910 was never true

911 update_or_create(article, site, new_deployed_date) 

912 else: 

913 for article in container.article_set.all(): 

914 update_or_create(article, site, new_deployed_date) 

915 

916 

917def assign_doi(pid): 

918 """ 

919 In the Mersenne process, articles imported for the first time receive a DOI. 

920 Thus function creates a new DOI, based on the last doi stored in the Collection object. 

921 :param pid: 

922 :return: A new DOI, equal to 10.5802/@pid.(last_doi + 1) 

923 """ 

924 collection = get_collection(pid) 

925 

926 if collection is None: 926 ↛ 927line 926 didn't jump to line 927, because the condition on line 926 was never true

927 return None 

928 

929 last_doi = collection.last_doi + 1 

930 collection.last_doi = last_doi 

931 collection.save() 

932 

933 doi = "10.5802/" + pid.lower() + "." + str(last_doi) 

934 return doi 

935 

936 

937# TODO make a command ? 

938 

939 

940def add_or_update_extid( 

941 resource, id_type, id_value, checked, false_positive, update_last_modified=True 

942): 

943 from ptf.cmds import database_cmds 

944 

945 if id_value: 945 ↛ exitline 945 didn't return from function 'add_or_update_extid', because the condition on line 945 was never false

946 extid = get_extid(resource, id_type) 

947 if extid: 

948 if not extid.checked: 948 ↛ 949line 948 didn't jump to line 949, because the condition on line 948 was never true

949 extid.id_value = id_value 

950 extid.checked = checked 

951 extid.false_positive = false_positive 

952 extid.save() 

953 else: 

954 cmd = database_cmds.addExtIdDatabaseCmd( 

955 { 

956 "id_type": id_type, 

957 "id_value": id_value, 

958 "checked": checked, 

959 "false_positive": false_positive, 

960 } 

961 ) 

962 cmd.set_resource(resource) 

963 cmd.do() 

964 

965 # last_modified is not modified during data restoration (importExtraDataPtfCmd) 

966 if update_last_modified: 966 ↛ 967line 966 didn't jump to line 967, because the condition on line 966 was never true

967 post_resource_updated(resource) 

968 

969 

970def add_or_update_bibitemid( 

971 bibitem, id_type, id_value, checked, false_positive, update_last_modified=True 

972): 

973 from ptf.cmds import database_cmds 

974 from ptf.cmds import xml_cmds 

975 

976 if id_value: 976 ↛ exitline 976 didn't return from function 'add_or_update_bibitemid', because the condition on line 976 was never false

977 bibitemid = get_bibitemid(bibitem, id_type) 

978 if bibitemid: 

979 bibitemid.id_value = id_value 

980 bibitemid.checked = checked 

981 bibitemid.false_positive = false_positive 

982 bibitemid.save() 

983 else: 

984 cmd = database_cmds.addBibItemIdDatabaseCmd( 

985 { 

986 "id_type": id_type, 

987 "id_value": id_value, 

988 "checked": checked, 

989 "false_positive": false_positive, 

990 } 

991 ) 

992 cmd.set_bibitem(bibitem) 

993 cmd.do() 

994 

995 # Update citation_xml|html|tex 

996 cmd = xml_cmds.updateBibitemCitationXmlCmd() 

997 cmd.set_bibitem(bibitem) 

998 cmd.do() 

999 

1000 # last_modified is not modified during data restoration (importExtraDataPtfCmd) 

1001 if update_last_modified: 

1002 post_resource_updated(bibitem.resource) 

1003 

1004 

1005def get_site_id(collection_id): 

1006 result = [v for k, v in SITE_REGISTER.items() if v["collection_pid"] == collection_id] 

1007 return result[0]["site_id"] if result else "" 

1008 

1009 

1010def get_collection_id(site_id): 

1011 result = [v for k, v in SITE_REGISTER.items() if v["site_id"] == site_id] 

1012 return result[0]["collection_pid"] if result else "" 

1013 

1014 

1015def is_site_en_only(site_id): 

1016 result = [v for k, v in SITE_REGISTER.items() if v["site_id"] == site_id] 

1017 return result[0]["en_only"] if result else False 

1018 

1019 

1020def is_site_fr_only(site_id): 

1021 result = [v for k, v in SITE_REGISTER.items() if v["site_id"] == site_id] 

1022 return result[0]["fr_only"] if result and "fr_only" in result[0] else False