from uuid import uuid4 import requests from django.apps import apps from django.core.files.base import ContentFile from django.db import models from django.urls import reverse from django_extensions.db.models import TimeStampedModel from imagekit.models import ImageSpecField from imagekit.processors import ResizeToFit from puzzles.sources import ipdb from scrobbles.dataclasses import PuzzleLogData from scrobbles.mixins import ScrobblableConstants, ScrobblableMixin BNULL = {"blank": True, "null": True} class PuzzleManufacturer(TimeStampedModel): uuid = models.UUIDField(default=uuid4, editable=False, **BNULL) name = models.CharField(max_length=255) ipdb_id = models.CharField(max_length=200, **BNULL) description = models.TextField(**BNULL) def __str__(self) -> str: return str(self.name) class Puzzle(ScrobblableMixin): description = models.TextField(**BNULL) orientation = models.CharField(max_length=50, **BNULL) dimensions_in_inches = models.CharField(max_length=30, **BNULL) publish_year = models.IntegerField(**BNULL) material = models.CharField(max_length=50, **BNULL) cut_style = models.CharField(max_length=100, **BNULL) pieces_count = models.IntegerField(**BNULL) ipdb_id = models.CharField(max_length=255, **BNULL) barcode = models.CharField(max_length=13, **BNULL) ipdb_image = models.ImageField(upload_to="puzzles/ipdb/", **BNULL) ipdb_image_small = ImageSpecField( source="ipdb_image", processors=[ResizeToFit(100, 100)], format="JPEG", options={"quality": 60}, ) ipdb_image_medium = ImageSpecField( source="ipdb_image", processors=[ResizeToFit(300, 300)], format="JPEG", options={"quality": 75}, ) manufacturer = models.ForeignKey( PuzzleManufacturer, on_delete=models.DO_NOTHING, **BNULL ) def get_absolute_url(self) -> str: return reverse("puzzles:puzzle_detail", kwargs={"slug": self.uuid}) def __str__(self): return f"{self.title} ({self.pieces_count}) by {self.manufacturer}" @property def subtitle(self): return self.manufacturer.name @property def strings(self) -> ScrobblableConstants: return ScrobblableConstants(verb="Solving", tags="puzzle") @property def ipdb_link(self) -> str: link = "" if self.ipdb_id: link = f"https://www.ipdb.plus/IPDb/puzzle.php?id={self.ipdb_id}" return link @property def primary_image_url(self) -> str: url = "" if self.ipdb_image: url = self.ipdb_image.url return url @property def logdata_cls(self): return PuzzleLogData @classmethod def find_or_create(cls, ipdb_id: str) -> "Puzzle": puzzle = cls.objects.filter(ipdb_id=ipdb_id).first() if not puzzle: puzzle_dict = ipdb.get_puzzle_from_ipdb_id(ipdb_id) manufacturer_name = puzzle_dict.pop("manufacturer", None) if manufacturer_name: ( manufacturer, _created, ) = PuzzleManufacturer.objects.get_or_create( name=manufacturer_name ) puzzle_dict["manufacturer_id"] = manufacturer.id genres = puzzle_dict.pop("genres", None) cover_url = puzzle_dict.pop("ipdb_image_url", None) puzzle = Puzzle.objects.create(**puzzle_dict) if genres: puzzle.genre.add(*genres) if cover_url: r = requests.get(cover_url) if r.status_code == 200: fname = f"{puzzle.title}_{puzzle.uuid}.jpg" puzzle.ipdb_image.save( fname, ContentFile(r.content), save=True ) return puzzle def scrobbles(self, user_id): Scrobble = apps.get_model("scrobbles", "Scrobble") return Scrobble.objects.filter(user_id=user_id, puzzle=self).order_by( "-timestamp" )