models.py 3.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. import logging
  2. import requests
  3. from books.openlibrary import lookup_book_from_openlibrary
  4. from django.conf import settings
  5. from django.contrib.auth import get_user_model
  6. from django.core.files.base import ContentFile
  7. from django.db import models
  8. from django.urls import reverse
  9. from django_extensions.db.models import TimeStampedModel
  10. from scrobbles.mixins import ScrobblableMixin
  11. from scrobbles.utils import get_scrobbles_for_media
  12. logger = logging.getLogger(__name__)
  13. User = get_user_model()
  14. BNULL = {"blank": True, "null": True}
  15. class Author(TimeStampedModel):
  16. name = models.CharField(max_length=255)
  17. openlibrary_id = models.CharField(max_length=255, **BNULL)
  18. def __str__(self):
  19. return f"{self.name}"
  20. def fix_metadata(self):
  21. logger.warn("Not implemented yet")
  22. class Book(ScrobblableMixin):
  23. COMPLETION_PERCENT = getattr(settings, "BOOK_COMPLETION_PERCENT", 95)
  24. title = models.CharField(max_length=255)
  25. authors = models.ManyToManyField(Author)
  26. goodreads_id = models.CharField(max_length=255, **BNULL)
  27. koreader_id = models.IntegerField(**BNULL)
  28. koreader_authors = models.CharField(max_length=255, **BNULL)
  29. koreader_md5 = models.CharField(max_length=255, **BNULL)
  30. isbn = models.CharField(max_length=255, **BNULL)
  31. pages = models.IntegerField(**BNULL)
  32. language = models.CharField(max_length=4, **BNULL)
  33. first_publish_year = models.IntegerField(**BNULL)
  34. author_name = models.CharField(max_length=255, **BNULL)
  35. author_openlibrary_id = models.CharField(max_length=255, **BNULL)
  36. openlibrary_id = models.CharField(max_length=255, **BNULL)
  37. cover = models.ImageField(upload_to="books/covers/", **BNULL)
  38. def __str__(self):
  39. return f"{self.title} by {self.author}"
  40. def fix_metadata(self, force_update=False):
  41. if not self.openlibrary_id or force_update:
  42. book_dict = lookup_book_from_openlibrary(self.title, self.author)
  43. cover_url = book_dict.pop("cover_url")
  44. Book.objects.filter(pk=self.id).update(**book_dict)
  45. r = requests.get(cover_url)
  46. if r.status_code == 200:
  47. fname = f"{self.title}_{self.uuid}.jpg"
  48. self.cover.save(fname, ContentFile(r.content), save=True)
  49. self.save()
  50. @property
  51. def author(self):
  52. return self.authors.first()
  53. def get_absolute_url(self):
  54. return reverse("books:book_detail", kwargs={"slug": self.uuid})
  55. @property
  56. def pages_for_completion(self) -> int:
  57. if not self.pages:
  58. logger.warn(f"{self} has no pages, no completion percentage")
  59. return 0
  60. return int(self.pages * (self.COMPLETION_PERCENT / 100))
  61. def progress_for_user(self, user_id: int) -> int:
  62. """Used to keep track of whether the book is complete or not"""
  63. user = User.objects.get(id=user_id)
  64. last_scrobble = get_scrobbles_for_media(self, user).last()
  65. return int((last_scrobble.book_pages_read / self.pages) * 100)
  66. @classmethod
  67. def find_or_create(cls, data_dict: dict) -> "Game":
  68. from books.utils import get_or_create_book
  69. return get_or_create_book(
  70. data_dict.get("title"), data_dict.get("author")
  71. )