Coverage for src/ptf/views/components/breadcrumb.py: 75%

222 statements  

« prev     ^ index     » next       coverage.py v7.6.4, created at 2024-11-05 09:56 +0000

1from __future__ import annotations 

2 

3from dataclasses import dataclass 

4from dataclasses import field 

5 

6from django.conf import settings 

7from django.urls import reverse 

8from django.urls import reverse_lazy 

9from django.utils import timezone 

10from django.utils.translation import gettext as translate_text 

11from django.utils.translation import gettext_lazy as _ 

12 

13from ptf.model_helpers import get_issues_in_volume 

14from ptf.utils import volume_display 

15 

16 

17@dataclass 

18class BreadcrumbItem: 

19 """ 

20 Interface for a breadcrumb item. 

21 """ 

22 

23 title: str 

24 url: str 

25 icon_class: str = field(default="") 

26 html: bool = field(default=False) 

27 

28 def __init__(self, title: str, url: str, icon_class: str = "", html=False): 

29 self.title = title 

30 self.url = url 

31 

32 

33@dataclass 

34class Breadcrumb: 

35 """ 

36 Interface for Breadcrumb data. 

37 """ 

38 

39 previousItem: BreadcrumbItem = None 

40 nextItem: BreadcrumbItem = None 

41 items: list[BreadcrumbItem] = field(default_factory=list) 

42 

43 def add_item(self, title: str, url: str, icon_class: str = "", html=False): 

44 self.items.append(BreadcrumbItem(title=title, url=url, icon_class=icon_class, html=html)) 

45 

46 

47def get_base_breadcrumb() -> Breadcrumb: 

48 """ 

49 Returns a breadcrumb with the default "Home" breadcrumb item. 

50 """ 

51 return Breadcrumb( 

52 items=[ 

53 BreadcrumbItem( 

54 title=_("Home"), url=reverse_lazy("mesh:home"), icon_class="fa-house-chimney" 

55 ) 

56 ] 

57 ) 

58 

59 

60# logging.basicConfig(level=logging.DEBUG) 

61 

62 

63def get_breadcrumb(resource, is_volume=False): # noqa: F811 (TODO: remove above function) 

64 """ 

65 Solution with a visitor 

66 """ 

67 if settings.SITE_NAME in ["crchim", "crphys", "crmeca", "crbiol", "crgeos", "crmath"]: 

68 visitor = BreadcrumbVisitorCR() 

69 else: 

70 visitor = BreadcrumbVisitor() 

71 

72 if is_volume: 

73 resource = Volume(resource) 

74 

75 crumb = resource.accept(visitor) 

76 

77 return crumb 

78 

79 

80######################################################################################### 

81# 

82# Other solutions 

83# 

84# Solution 1: 

85# BCBuilder with functions dynamically added to the Article/Container/Collection 

86# 

87# Solution 2: 

88# BCBuilder Article/Container/Collection mixin dynamically added the base classes 

89# a VolumeBCMixin class is added to handle volumes 

90# 

91# Solution 3: 

92# A visitor 

93# 

94######################################################################################### 

95 

96''' 

97class BCBuilder: 

98 def __init__(self): 

99 self.crumb = Breadcrumb() 

100 

101 def add_article_bcitem(self, article): 

102 display_page = True 

103 if hasattr(settings, "PAGE_BREADCRUMB"): 

104 display_page = settings.PAGE_BREADCRUMB 

105 if display_page: 

106 href = article.get_absolute_url() 

107 title = article.get_breadcrumb_page_text() 

108 self.crumb.add_item(title, href) 

109 

110 def add_collection_bcitem(self, collection): 

111 href = collection.get_absolute_url() 

112 

113 if collection.pid == "MALSM": 

114 href = reverse("malsm_books") 

115 

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

117 # Numdam, Geodesic, Proceedings 

118 if settings.COLLECTION_PID in ["ALL", "PROCEEDINGS"]: 

119 title = collection.title_html 

120 elif collection.pid in settings.CRAS_COLLECTIONS: 

121 title = _("Consulter") 

122 else: 

123 title = _("Feuilleter") 

124 

125 self.crumb.add_item(title, href) 

126 

127 def add_volume_bcitem(self, container): 

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

129 volume = VolumeBCMixin(container) 

130 href = volume.get_absolute_url() 

131 

132 if hasattr(settings, "YEAR_BREADCRUMB"): 

133 title = str(container.year) 

134 else: 

135 title = ( 

136 "{} {} : ".format(_("Série"), container.vseries) if container.vseries else "" 

137 ) 

138 if container.volume: 

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

140 else: 

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

142 self.crumb.add_item(title, href) 

143 

144 def add_container_bcitem(self, container): 

145 title = "" 

146 href = "" 

147 

148 colid = container.get_top_collection().pid 

149 if colid in settings.CRAS_COLLECTIONS and not container.title_html: 

150 year = int(container.year) 

151 if (colid != "CRBIOL" and year > 2020) or (colid == "CRBIOL" and year > 2022): 

152 now = timezone.now() 

153 curyear = str(now.year) 

154 if curyear == container.year: 

155 title = translate_text("Articles du volume en cours") 

156 else: 

157 title = translate_text("Articles du volume") 

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

159 

160 if not title and container.number: 

161 title = "no." + " " + container.number 

162 href = container.get_absolute_url() 

163 

164 if title: 

165 self.crumb.add_item(title, href) 

166 

167 

168############################################################################# 

169# 

170# Solution 1: BCBuilder with functions 

171# (not complete, need to extend the resources with get_next and get_previous) 

172# 

173############################################################################# 

174 

175 

176# def article_build_breadcrumb(self): 

177# collection = self.get_top_collection() 

178# container = self.get_container() 

179# 

180# breadcrumb_builder = BCBuilder() 

181# 

182# breadcrumb_builder.add_collection_bcitem(collection) 

183# breadcrumb_builder.add_container_bcitem(container) 

184# breadcrumb_builder.add_article_bcitem(self) 

185# 

186# breadcrumb_builder.set_next_previous_article_bc_items(self) 

187# 

188# return breadcrumb_builder 

189# 

190# 

191# def container_build_breadcrumb(self): 

192# collection = self.get_top_collection() 

193# 

194# breadcrumb_builder = BCBuilder() 

195# 

196# breadcrumb_builder.add_collection_bcitem(collection) 

197# breadcrumb_builder.add_container_bcitem(self) 

198# 

199# breadcrumb_builder.set_next_previous_container_bc_items(self) 

200# 

201# return breadcrumb_builder 

202# 

203# 

204# def collection_build_breadcrumb(self): 

205# breadcrumb_builder = BCBuilder() 

206# 

207# breadcrumb_builder.add_collection_bcitem(self) 

208# 

209# return breadcrumb_builder 

210 

211# Article.build_breadcrumb = article_build_breadcrumb 

212# Article.get_next = article_get_next 

213# Container.build_breadcrumb = container_build_breadcrumb 

214# Container.get_next = container_get_next 

215# Collection.build_breadcrumb = collection_build_breadcrumb 

216 

217 

218########################################### 

219# 

220# Solution 2: BCBuilder with mixin 

221# 

222########################################### 

223 

224 

225class ResourceBCMixin: 

226 def get_next(self): 

227 return None 

228 

229 def get_previous(self): 

230 return None 

231 

232 def _next_in_qs(self, qs): 

233 next_item = None 

234 

235 if qs.count() > 1: 

236 ready_for_next = False 

237 for item in qs: 

238 if ready_for_next: 

239 next_item = item 

240 ready_for_next = False 

241 if item.pid == self.pid: 

242 ready_for_next = True 

243 return next_item 

244 

245 

246class ArticleBCMixin(ResourceBCMixin): 

247 def build_breadcrumb(self): 

248 collection = self.get_top_collection() 

249 container = self.get_container() 

250 

251 breadcrumb_builder = BCBuilder() 

252 

253 breadcrumb_builder.add_collection_bcitem(collection) 

254 breadcrumb_builder.add_volume_bcitem(container) 

255 breadcrumb_builder.add_container_bcitem(container) 

256 breadcrumb_builder.add_article_bcitem(self) 

257 

258 self.set_next_previous_bc_items(breadcrumb_builder.crumb) 

259 

260 return breadcrumb_builder.crumb 

261 

262 def get_next(self): 

263 next_article = None 

264 

265 try: 

266 next_article = self.my_container.article_set.get(seq=(self.seq + 1)) 

267 except (Article.DoesNotExist, MultipleObjectsReturned): 

268 pass 

269 

270 if next_article is None and not self.my_container.title_html and self.my_container.volume: 

271 qs = Container.objects.filter(volume=self.my_container.volume) 

272 if qs.count() > 1: 

273 qs = qs.order_by("number_int") 

274 next_issue = self._next_in_qs(qs) 

275 if next_issue: 

276 qs = next_issue.article_set.order_by("seq") 

277 if qs.exists(): 

278 next_article = qs.first() 

279 

280 return next_article 

281 

282 def get_previous(self): 

283 previous_article = None 

284 

285 try: 

286 previous_article = self.my_container.article_set.get(seq=(self.seq - 1)) 

287 except (Article.DoesNotExist, MultipleObjectsReturned): 

288 pass 

289 

290 if ( 

291 previous_article is None 

292 and not self.my_container.title_html 

293 and self.my_container.volume 

294 ): 

295 qs = Container.objects.filter(volume=self.my_container.volume) 

296 if qs.count() > 1: 

297 qs = qs.order_by("-number_int") 

298 previous_issue = self._next_in_qs(qs) 

299 if previous_issue: 

300 qs = previous_issue.article_set.order_by("-seq") 

301 if qs.exists(): 

302 previous_article = qs.first() 

303 

304 return previous_article 

305 

306 def set_next_previous_bc_items(self, crumb): 

307 next_article = self.get_next() 

308 if next_article is not None: 

309 crumb.nextItem = BreadcrumbItem( 

310 translate_text("Suivant"), next_article.get_absolute_url() 

311 ) 

312 

313 previous_article = self.get_previous() 

314 if previous_article is not None: 

315 crumb.previousItem = BreadcrumbItem( 

316 translate_text("Précédent"), previous_article.get_absolute_url() 

317 ) 

318 

319 # Les articles de CRAS des volumes généraux sont mis dans plusieurs containers (G1, G2, ...) 

320 # On regarde s'il y a un numéro (caché) suivant/précédant pour y prendre le premier/dernier article 

321 if (next_article is None or previous_article is None) and not self.my_container.title_html: 

322 issues_article, collection = get_issues_in_volume(self.my_container.pid, True, True) 

323 

324 if next_article is None: 

325 return_next = None 

326 current_found = False 

327 index = 0 

328 articles = issues_article[0]["articles"] 

329 while index < len(articles) and return_next is None: 

330 if current_found: 

331 return_next = articles[index] 

332 if articles[index].pid == self.pid: 

333 current_found = True 

334 index += 1 

335 if return_next is not None: 

336 crumb.nextItem = BreadcrumbItem( 

337 translate_text("Suivant"), return_next.get_absolute_url() 

338 ) 

339 if previous_article is None: 

340 return_prev = None 

341 current_found = False 

342 articles = issues_article[0]["articles"] 

343 index = len(articles) - 1 

344 while index >= 0 and return_prev is None: 

345 if current_found: 

346 return_prev = articles[index] 

347 if articles[index].pid == self.pid: 

348 current_found = True 

349 index -= 1 

350 if return_prev is not None: 

351 crumb.previousItem = BreadcrumbItem( 

352 translate_text("Précédent"), return_prev.get_absolute_url() 

353 ) 

354 

355 

356class ContainerBCMixin(ResourceBCMixin): 

357 def build_breadcrumb(self): 

358 collection = self.get_top_collection() 

359 

360 breadcrumb_builder = BCBuilder() 

361 

362 breadcrumb_builder.add_collection_bcitem(collection) 

363 breadcrumb_builder.add_volume_bcitem(self) 

364 breadcrumb_builder.add_container_bcitem(self) 

365 

366 self.set_next_previous_bc_items(breadcrumb_builder.crumb) 

367 

368 return breadcrumb_builder.crumb 

369 

370 def get_next(self): 

371 # No Next/Previous for CRAS Special Issues or "Volume articles" 

372 colid = self.get_top_collection().pid 

373 if colid in settings.CRAS_COLLECTIONS: 

374 year = int(self.year) 

375 if ( 

376 self.title_html 

377 or (colid != "CRBIOL" and year > 2020) 

378 or (colid == "CRBIOL" and year > 2022) 

379 ): 

380 return None 

381 

382 collection = self.get_top_collection() 

383 if collection.pid in settings.COLLECTIONS_SEQUENCED: 

384 qs = collection.content.order_by("seq") 

385 else: 

386 qs = collection.content.order_by("vseries_int", "year", "volume_int", "number_int") 

387 

388 next_issue = self._next_in_qs(qs) 

389 return next_issue 

390 

391 def get_previous(self): 

392 # No Next/Previous for CRAS Special Issues or "Volume articles" 

393 colid = self.get_top_collection().pid 

394 if colid in settings.CRAS_COLLECTIONS: 

395 year = int(self.year) 

396 if ( 

397 self.title_html 

398 or (colid != "CRBIOL" and year > 2020) 

399 or (colid == "CRBIOL" and year > 2022) 

400 ): 

401 return None 

402 

403 collection = self.get_top_collection() 

404 if collection.pid in settings.COLLECTIONS_SEQUENCED: 

405 qs = collection.content.order_by("-seq") 

406 else: 

407 qs = collection.content.order_by("-vseries_int", "-year", "-volume_int", "-number_int") 

408 next_issue = self._next_in_qs(qs) 

409 return next_issue 

410 

411 def set_next_previous_bc_items(self, crumb): 

412 next_container = self.get_next() 

413 if next_container is not None: 

414 title = translate_text("Suivant") 

415 href = next_container.get_absolute_url() 

416 crumb.nextItem = BreadcrumbItem(title, href) 

417 

418 previous_container = self.get_previous() 

419 if previous_container is not None: 

420 title = translate_text("Précédent") 

421 href = previous_container.get_absolute_url() 

422 crumb.previousItem = BreadcrumbItem(title, href) 

423 

424 

425class VolumeBCMixin(ContainerBCMixin): 

426 """ 

427 There is no Volume class in the PTF Model. 

428 VolumeBCMixin is not a mixin but a full class. 

429 In order to work with get_breadcrumb, we need to add Resource functions such as get_absolute_url 

430 """ 

431 

432 def __init__(self, container_bc, **kwargs): 

433 super().__init__(**kwargs) 

434 self.container_bc = container_bc 

435 

436 def __getattribute__(self, name): 

437 try: 

438 value = super().__getattribute__(name) 

439 except AttributeError: 

440 value = self.container_bc.__getattribute__(name) 

441 return value 

442 

443 def build_breadcrumb(self): 

444 collection = self.container_bc.get_top_collection() 

445 

446 breadcrumb_builder = BCBuilder() 

447 

448 breadcrumb_builder.add_collection_bcitem(collection) 

449 breadcrumb_builder.add_volume_bcitem(self) 

450 

451 self.set_next_previous_bc_items(breadcrumb_builder.crumb) 

452 

453 return breadcrumb_builder.crumb 

454 

455 def get_absolute_url(self): 

456 if hasattr(settings, "YEAR_BREADCRUMB"): 

457 href = reverse("articles-year", kwargs={"year": self.container_bc.year}) 

458 else: 

459 href = reverse("volume-items", kwargs={"vid": self.container_bc.get_vid()}) 

460 

461 return href 

462 

463 def _next_in_volume_qs(self, qs): 

464 next_item = None 

465 

466 if qs.count() > 1: 

467 ready_for_next = False 

468 for item in qs: 

469 if ready_for_next and ( 

470 item.year != self.container_bc.year or item.volume != self.container_bc.volume 

471 ): 

472 next_item = item 

473 ready_for_next = False 

474 if item.pid == self.container_bc.pid: 

475 ready_for_next = True 

476 return next_item 

477 

478 def get_next(self): 

479 next_container = None 

480 collection = self.container_bc.get_top_collection() 

481 if collection.pid in settings.COLLECTIONS_SEQUENCED: 

482 next_container = self.container_bc.get_next() 

483 else: 

484 qs = collection.content.order_by("vseries_int", "year", "volume_int", "number_int") 

485 next_container = self._next_in_volume_qs(qs) 

486 

487 if next_container: 

488 # Convert to VolumeBCMixin to have access to the "volume" get_absolute_url 

489 next_container = VolumeBCMixin(next_container) 

490 

491 return next_container 

492 

493 def get_previous(self): 

494 previous_container = None 

495 collection = self.container_bc.get_top_collection() 

496 if collection.pid in settings.COLLECTIONS_SEQUENCED: 

497 previous_container = self.container_bc.get_previous() 

498 else: 

499 qs = collection.content.order_by("-vseries_int", "-year", "-volume_int", "-number_int") 

500 previous_container = self._next_in_volume_qs(qs) 

501 

502 if previous_container: 

503 # Convert to VolumeBCMixin to have access to the "volume" get_absolute_url 

504 previous_container = VolumeBCMixin(previous_container) 

505 

506 return previous_container 

507 

508 

509class CollectionBCMixin(ResourceBCMixin): 

510 def build_breadcrumb(self): 

511 breadcrumb_builder = BCBuilder() 

512 

513 breadcrumb_builder.add_collection_bcitem(self) 

514 

515 return breadcrumb_builder.crumb 

516 

517''' 

518########################################### 

519# 

520# Solution 3: a visitor 

521# 

522########################################### 

523 

524 

525class BreadcrumbVisitor: 

526 def __init__(self): 

527 self.crumb = Breadcrumb() 

528 

529 def visit(self, resource): 

530 meth = getattr(self, "visit_" + resource.classname.lower()) 

531 return meth(resource) 

532 

533 def visit_article(self, article): 

534 collection = article.get_top_collection() 

535 container = article.get_container() 

536 

537 self._add_collection_bcitem(collection) 

538 self._add_volume_bcitem(container) 

539 self._add_container_bcitem(container) 

540 self._add_article_bcitem(article) 

541 

542 next_article, previous_article = self.set_next_previous_article_bc_items(article) 

543 if next_article is not None: 543 ↛ 544line 543 didn't jump to line 544 because the condition on line 543 was never true

544 self.crumb.nextItem = BreadcrumbItem( 

545 translate_text("Suivant"), next_article.get_absolute_url() 

546 ) 

547 

548 if previous_article is not None: 548 ↛ 549line 548 didn't jump to line 549 because the condition on line 548 was never true

549 self.crumb.previousItem = BreadcrumbItem( 

550 translate_text("Précédent"), previous_article.get_absolute_url() 

551 ) 

552 

553 return self.crumb 

554 

555 def visit_container(self, container): 

556 collection = container.get_top_collection() 

557 

558 self._add_collection_bcitem(collection) 

559 self._add_volume_bcitem(container) 

560 self._add_container_bcitem(container) 

561 

562 self.set_next_previous_container_bc_items(container) 

563 

564 return self.crumb 

565 

566 def visit_volume(self, volume): 

567 collection = volume.get_top_collection() 

568 

569 self._add_collection_bcitem(collection) 

570 self._add_volume_bcitem(volume) 

571 

572 self.set_next_previous_container_bc_items(volume) 

573 

574 return self.crumb 

575 

576 def visit_collection(self, collection): 

577 self._add_collection_bcitem(collection) 

578 

579 return self.crumb 

580 

581 def _add_article_bcitem(self, article): 

582 display_page = True 

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

584 display_page = settings.PAGE_BREADCRUMB 

585 if display_page: 585 ↛ exitline 585 didn't return from function '_add_article_bcitem' because the condition on line 585 was always true

586 href = article.get_absolute_url() 

587 title = article.get_breadcrumb_page_text() 

588 self.crumb.add_item(title, href) 

589 

590 def _add_collection_bcitem(self, collection): 

591 href = collection.get_absolute_url() 

592 

593 if collection.pid == "MALSM": 593 ↛ 594line 593 didn't jump to line 594 because the condition on line 593 was never true

594 href = reverse("malsm_books") 

595 

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

597 # Numdam, Geodesic, Proceedings 

598 if settings.COLLECTION_PID in ["ALL", "PROCEEDINGS"]: 598 ↛ 600line 598 didn't jump to line 600 because the condition on line 598 was always true

599 title = collection.title_html 

600 elif collection.pid in settings.CRAS_COLLECTIONS: 

601 title = _("Consulter") 

602 else: 

603 title = _("Feuilleter") 

604 

605 self.crumb.add_item(title, href) 

606 

607 def _add_volume_bcitem(self, container): 

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

609 volume = Volume(container) 

610 href = volume.get_absolute_url() 

611 

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

613 title = str(container.year) 

614 else: 

615 title = ( 

616 "{} {} : ".format(_("Série"), container.vseries) if container.vseries else "" 

617 ) 

618 if container.volume: 618 ↛ 619line 618 didn't jump to line 619 because the condition on line 618 was never true

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

620 else: 

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

622 self.crumb.add_item(title, href) 

623 

624 def _add_container_bcitem(self, container): 

625 title = "" 

626 href = "" 

627 

628 colid = container.get_top_collection().pid 

629 if colid in settings.CRAS_COLLECTIONS and not container.title_html: 

630 year = int(container.year) 

631 if (colid != "CRBIOL" and year > 2020) or (colid == "CRBIOL" and year > 2022): 

632 now = timezone.now() 

633 curyear = str(now.year) 

634 if curyear == container.year: 634 ↛ 635line 634 didn't jump to line 635 because the condition on line 634 was never true

635 title = translate_text("Articles du volume en cours") 

636 else: 

637 title = translate_text("Articles du volume") 

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

639 

640 if not title and container.number: 

641 title = "no." + " " + container.number 

642 href = container.get_absolute_url() 

643 

644 if title: 644 ↛ exitline 644 didn't return from function '_add_container_bcitem' because the condition on line 644 was always true

645 self.crumb.add_item(title, href) 

646 

647 def set_next_previous_container_bc_items(self, container): 

648 next_container = container.get_next_resource() 

649 if next_container is not None: 649 ↛ 650line 649 didn't jump to line 650 because the condition on line 649 was never true

650 title = translate_text("Suivant") 

651 href = next_container.get_absolute_url() 

652 self.crumb.nextItem = BreadcrumbItem(title, href) 

653 

654 previous_container = container.get_previous_resource() 

655 if previous_container is not None: 655 ↛ 656line 655 didn't jump to line 656 because the condition on line 655 was never true

656 title = translate_text("Précédent") 

657 href = previous_container.get_absolute_url() 

658 self.crumb.previousItem = BreadcrumbItem(title, href) 

659 

660 def set_next_previous_article_bc_items(self, article): 

661 next_article = article.get_next_resource() 

662 if next_article is not None: 662 ↛ 663line 662 didn't jump to line 663 because the condition on line 662 was never true

663 self.crumb.nextItem = BreadcrumbItem( 

664 translate_text("Suivant"), next_article.get_absolute_url() 

665 ) 

666 

667 previous_article = article.get_previous_resource() 

668 if previous_article is not None: 668 ↛ 669line 668 didn't jump to line 669 because the condition on line 668 was never true

669 self.crumb.previousItem = BreadcrumbItem( 

670 translate_text("Précédent"), previous_article.get_absolute_url() 

671 ) 

672 return next_article, previous_article 

673 

674 

675class BreadcrumbVisitorCR(BreadcrumbVisitor): 

676 def set_next_previous_article_bc_items(self, article): 

677 next_article, previous_article = super().set_next_previous_article_bc_items(article) 

678 # Les articles de CRAS des volumes généraux sont mis dans plusieurs containers (G1, G2, ...) 

679 # On regarde s'il y a un numéro (caché) suivant/précédant pour y prendre le premier/dernier article 

680 if not article.my_container.title_html: 680 ↛ 713line 680 didn't jump to line 713 because the condition on line 680 was always true

681 issues_article, collection = get_issues_in_volume(article.my_container.pid, True, True) 

682 

683 if next_article is None: 683 ↛ 698line 683 didn't jump to line 698 because the condition on line 683 was always true

684 return_next = None 

685 current_found = False 

686 index = 0 

687 articles = issues_article[0]["articles"] 

688 while index < len(articles) and return_next is None: 

689 if current_found: 689 ↛ 690line 689 didn't jump to line 690 because the condition on line 689 was never true

690 return_next = articles[index] 

691 if articles[index].pid == article.pid: 691 ↛ 693line 691 didn't jump to line 693 because the condition on line 691 was always true

692 current_found = True 

693 index += 1 

694 if return_next is not None: 694 ↛ 695line 694 didn't jump to line 695 because the condition on line 694 was never true

695 self.crumb.nextItem = BreadcrumbItem( 

696 translate_text("Suivant"), return_next.get_absolute_url() 

697 ) 

698 if previous_article is None: 698 ↛ 713line 698 didn't jump to line 713 because the condition on line 698 was always true

699 return_prev = None 

700 current_found = False 

701 articles = issues_article[0]["articles"] 

702 index = len(articles) - 1 

703 while index >= 0 and return_prev is None: 

704 if current_found: 704 ↛ 705line 704 didn't jump to line 705 because the condition on line 704 was never true

705 return_prev = articles[index] 

706 if articles[index].pid == article.pid: 706 ↛ 708line 706 didn't jump to line 708 because the condition on line 706 was always true

707 current_found = True 

708 index -= 1 

709 if return_prev is not None: 709 ↛ 710line 709 didn't jump to line 710 because the condition on line 709 was never true

710 self.crumb.previousItem = BreadcrumbItem( 

711 translate_text("Précédent"), return_prev.get_absolute_url() 

712 ) 

713 return next_article, previous_article 

714 

715 

716class Volume: 

717 """ 

718 There is no Volume class in the PTF Model. 

719 Add it here to handle breadcrumb code for volumes 

720 """ 

721 

722 def __init__(self, container, **kwargs): 

723 super().__init__(**kwargs) 

724 self.container = container 

725 self.classname = "Volume" 

726 

727 def __getattribute__(self, name): 

728 try: 

729 value = super().__getattribute__(name) 

730 except AttributeError: 

731 value = self.container.__getattribute__(name) 

732 return value 

733 

734 def cast(self): 

735 return self 

736 

737 def accept(self, visitor): 

738 """ 

739 We need to copy the resource:accept function here. 

740 If we do not, the call to volume.accept() will look for accept. 

741 The __get__attribute__ function will find the accept function of the encapsulated container function 

742 => accept will be called for the encapsulated container 

743 """ 

744 return visitor.visit(self) 

745 

746 def get_absolute_url(self): 

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

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

749 else: 

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

751 

752 return href 

753 

754 def _next_in_volume_qs(self, qs): 

755 next_item = None 

756 

757 if qs.count() > 1: 757 ↛ 758line 757 didn't jump to line 758 because the condition on line 757 was never true

758 ready_for_next = False 

759 for item in qs: 

760 if ready_for_next and ( 

761 item.year != self.container.year or item.volume != self.container.volume 

762 ): 

763 next_item = item 

764 ready_for_next = False 

765 if item.pid == self.container.pid: 

766 ready_for_next = True 

767 return next_item 

768 

769 def get_next_resource(self): 

770 next_container = None 

771 collection = self.container.get_top_collection() 

772 if collection.pid in settings.COLLECTIONS_SEQUENCED: 772 ↛ 773line 772 didn't jump to line 773 because the condition on line 772 was never true

773 next_container = self.container.get_next_resource() 

774 else: 

775 qs = collection.content.order_by("vseries_int", "year", "volume_int", "number_int") 

776 next_container = self._next_in_volume_qs(qs) 

777 

778 if next_container: 778 ↛ 780line 778 didn't jump to line 780 because the condition on line 778 was never true

779 # Convert to VolumeBCMixin to have access to the "volume" get_absolute_url 

780 next_container = Volume(next_container) 

781 

782 return next_container 

783 

784 def get_previous_resource(self): 

785 previous_container = None 

786 collection = self.container.get_top_collection() 

787 if collection.pid in settings.COLLECTIONS_SEQUENCED: 787 ↛ 788line 787 didn't jump to line 788 because the condition on line 787 was never true

788 previous_container = self.container.get_previous_resource() 

789 else: 

790 qs = collection.content.order_by("-vseries_int", "-year", "-volume_int", "-number_int") 

791 previous_container = self._next_in_volume_qs(qs) 

792 

793 if previous_container: 793 ↛ 795line 793 didn't jump to line 795 because the condition on line 793 was never true

794 # Convert to VolumeBCMixin to have access to the "volume" get_absolute_url 

795 previous_container = Volume(previous_container) 

796 

797 return previous_container