Files
vrobbler/vrobbler/apps/puzzles/models.py

138 lines
4.3 KiB
Python

from dataclasses import dataclass
from typing import Optional
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 JSONDataclass
from scrobbles.mixins import ScrobblableConstants, ScrobblableMixin
BNULL = {"blank": True, "null": True}
@dataclass
class PuzzleLogData(JSONDataclass):
with_people: Optional[int] = None
rating: Optional[str] = None
notes: Optional[str] = None
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 logdata_cls(self):
return PuzzleLogData
@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"
)