models.py 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. from uuid import uuid4
  2. import requests
  3. from django.apps import apps
  4. from django.core.files.base import ContentFile
  5. from django.db import models
  6. from django.urls import reverse
  7. from django_extensions.db.models import TimeStampedModel
  8. from imagekit.models import ImageSpecField
  9. from imagekit.processors import ResizeToFit
  10. from puzzles.sources import ipdb
  11. from scrobbles.dataclasses import PuzzleLogData
  12. from scrobbles.mixins import ScrobblableConstants, ScrobblableMixin
  13. BNULL = {"blank": True, "null": True}
  14. class PuzzleManufacturer(TimeStampedModel):
  15. uuid = models.UUIDField(default=uuid4, editable=False, **BNULL)
  16. name = models.CharField(max_length=255)
  17. ipdb_id = models.CharField(max_length=200, **BNULL)
  18. description = models.TextField(**BNULL)
  19. def __str__(self) -> str:
  20. return str(self.name)
  21. class Puzzle(ScrobblableMixin):
  22. description = models.TextField(**BNULL)
  23. orientation = models.CharField(max_length=50, **BNULL)
  24. dimensions_in_inches = models.CharField(max_length=30, **BNULL)
  25. publish_year = models.IntegerField(**BNULL)
  26. material = models.CharField(max_length=50, **BNULL)
  27. cut_style = models.CharField(max_length=100, **BNULL)
  28. pieces_count = models.IntegerField(**BNULL)
  29. ipdb_id = models.CharField(max_length=255, **BNULL)
  30. barcode = models.CharField(max_length=13, **BNULL)
  31. ipdb_image = models.ImageField(upload_to="puzzles/ipdb/", **BNULL)
  32. ipdb_image_small = ImageSpecField(
  33. source="ipdb_image",
  34. processors=[ResizeToFit(100, 100)],
  35. format="JPEG",
  36. options={"quality": 60},
  37. )
  38. ipdb_image_medium = ImageSpecField(
  39. source="ipdb_image",
  40. processors=[ResizeToFit(300, 300)],
  41. format="JPEG",
  42. options={"quality": 75},
  43. )
  44. manufacturer = models.ForeignKey(
  45. PuzzleManufacturer, on_delete=models.DO_NOTHING, **BNULL
  46. )
  47. def get_absolute_url(self) -> str:
  48. return reverse("puzzles:puzzle_detail", kwargs={"slug": self.uuid})
  49. def __str__(self):
  50. return f"{self.title} ({self.pieces_count}) by {self.manufacturer}"
  51. @property
  52. def subtitle(self):
  53. return self.manufacturer.name
  54. @property
  55. def strings(self) -> ScrobblableConstants:
  56. return ScrobblableConstants(verb="Solving", tags="puzzle")
  57. @property
  58. def ipdb_link(self) -> str:
  59. link = ""
  60. if self.ipdb_id:
  61. link = f"https://www.ipdb.plus/IPDb/puzzle.php?id={self.ipdb_id}"
  62. return link
  63. @property
  64. def primary_image_url(self) -> str:
  65. url = ""
  66. if self.ipdb_image:
  67. url = self.ipdb_image.url
  68. return url
  69. @property
  70. def logdata_cls(self):
  71. return PuzzleLogData
  72. @classmethod
  73. def find_or_create(cls, ipdb_id: str) -> "Puzzle":
  74. puzzle = cls.objects.filter(ipdb_id=ipdb_id).first()
  75. if not puzzle:
  76. puzzle_dict = ipdb.get_puzzle_from_ipdb_id(ipdb_id)
  77. manufacturer_name = puzzle_dict.pop("manufacturer", None)
  78. if manufacturer_name:
  79. (
  80. manufacturer,
  81. _created,
  82. ) = PuzzleManufacturer.objects.get_or_create(
  83. name=manufacturer_name
  84. )
  85. puzzle_dict["manufacturer_id"] = manufacturer.id
  86. genres = puzzle_dict.pop("genres", None)
  87. cover_url = puzzle_dict.pop("ipdb_image_url", None)
  88. puzzle = Puzzle.objects.create(**puzzle_dict)
  89. if genres:
  90. puzzle.genre.add(*genres)
  91. if cover_url:
  92. r = requests.get(cover_url)
  93. if r.status_code == 200:
  94. fname = f"{puzzle.title}_{puzzle.uuid}.jpg"
  95. puzzle.ipdb_image.save(
  96. fname, ContentFile(r.content), save=True
  97. )
  98. return puzzle
  99. def scrobbles(self, user_id):
  100. Scrobble = apps.get_model("scrobbles", "Scrobble")
  101. return Scrobble.objects.filter(user_id=user_id, puzzle=self).order_by(
  102. "-timestamp"
  103. )