[bricksets] Add brick set scrobble type
This commit is contained in:
0
vrobbler/apps/bricksets/__init__.py
Normal file
0
vrobbler/apps/bricksets/__init__.py
Normal file
23
vrobbler/apps/bricksets/admin.py
Normal file
23
vrobbler/apps/bricksets/admin.py
Normal file
@ -0,0 +1,23 @@
|
||||
from django.contrib import admin
|
||||
|
||||
from bricksets.models import BrickSet
|
||||
from scrobbles.admin import ScrobbleInline
|
||||
|
||||
|
||||
class BrickSetInline(admin.TabularInline):
|
||||
model = BrickSet
|
||||
extra = 0
|
||||
|
||||
|
||||
@admin.register(BrickSet)
|
||||
class BrickSetAdmin(admin.ModelAdmin):
|
||||
date_hierarchy = "created"
|
||||
list_display = (
|
||||
"uuid",
|
||||
"title",
|
||||
)
|
||||
ordering = ("-created",)
|
||||
search_fields = ("title",)
|
||||
inlines = [
|
||||
ScrobbleInline,
|
||||
]
|
||||
5
vrobbler/apps/bricksets/apps.py
Normal file
5
vrobbler/apps/bricksets/apps.py
Normal file
@ -0,0 +1,5 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class BrickSetsConfig(AppConfig):
|
||||
name = "bricksets"
|
||||
106
vrobbler/apps/bricksets/migrations/0001_initial.py
Normal file
106
vrobbler/apps/bricksets/migrations/0001_initial.py
Normal file
@ -0,0 +1,106 @@
|
||||
# Generated by Django 4.2.15 on 2024-09-07 05:38
|
||||
|
||||
from django.db import migrations, models
|
||||
import django_extensions.db.fields
|
||||
import taggit.managers
|
||||
import uuid
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
("scrobbles", "0059_remove_scrobble_book_koreader_hash_and_more"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="BrickSet",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
(
|
||||
"created",
|
||||
django_extensions.db.fields.CreationDateTimeField(
|
||||
auto_now_add=True, verbose_name="created"
|
||||
),
|
||||
),
|
||||
(
|
||||
"modified",
|
||||
django_extensions.db.fields.ModificationDateTimeField(
|
||||
auto_now=True, verbose_name="modified"
|
||||
),
|
||||
),
|
||||
(
|
||||
"uuid",
|
||||
models.UUIDField(
|
||||
blank=True,
|
||||
default=uuid.uuid4,
|
||||
editable=False,
|
||||
null=True,
|
||||
),
|
||||
),
|
||||
(
|
||||
"title",
|
||||
models.CharField(blank=True, max_length=255, null=True),
|
||||
),
|
||||
(
|
||||
"run_time_seconds",
|
||||
models.IntegerField(blank=True, null=True),
|
||||
),
|
||||
(
|
||||
"run_time_ticks",
|
||||
models.PositiveBigIntegerField(blank=True, null=True),
|
||||
),
|
||||
(
|
||||
"number",
|
||||
models.CharField(blank=True, max_length=10, null=True),
|
||||
),
|
||||
("release_year", models.IntegerField(blank=True, null=True)),
|
||||
("piece_count", models.IntegerField(blank=True, null=True)),
|
||||
(
|
||||
"brickset_rating",
|
||||
models.DecimalField(
|
||||
blank=True, decimal_places=1, max_digits=3, null=True
|
||||
),
|
||||
),
|
||||
(
|
||||
"lego_item_number",
|
||||
models.CharField(blank=True, max_length=10, null=True),
|
||||
),
|
||||
(
|
||||
"box_image",
|
||||
models.ImageField(
|
||||
blank=True, null=True, upload_to="brickset/boxes/"
|
||||
),
|
||||
),
|
||||
(
|
||||
"set_image",
|
||||
models.ImageField(
|
||||
blank=True, null=True, upload_to="brickset/sets/"
|
||||
),
|
||||
),
|
||||
(
|
||||
"genre",
|
||||
taggit.managers.TaggableManager(
|
||||
blank=True,
|
||||
help_text="A comma-separated list of tags.",
|
||||
through="scrobbles.ObjectWithGenres",
|
||||
to="scrobbles.Genre",
|
||||
verbose_name="Tags",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"abstract": False,
|
||||
},
|
||||
),
|
||||
]
|
||||
0
vrobbler/apps/bricksets/migrations/__init__.py
Normal file
0
vrobbler/apps/bricksets/migrations/__init__.py
Normal file
63
vrobbler/apps/bricksets/models.py
Normal file
63
vrobbler/apps/bricksets/models.py
Normal file
@ -0,0 +1,63 @@
|
||||
from django.apps import apps
|
||||
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 scrobbles.dataclasses import BrickSetLogData
|
||||
from scrobbles.mixins import LongPlayScrobblableMixin
|
||||
|
||||
BNULL = {"blank": True, "null": True}
|
||||
|
||||
|
||||
class BrickSet(LongPlayScrobblableMixin):
|
||||
""""""
|
||||
|
||||
number = models.CharField(max_length=10, **BNULL)
|
||||
release_year = models.IntegerField(**BNULL)
|
||||
piece_count = models.IntegerField(**BNULL)
|
||||
brickset_rating = models.DecimalField(max_digits=3, decimal_places=1, **BNULL)
|
||||
lego_item_number = models.CharField(max_length=10, **BNULL)
|
||||
box_image = models.ImageField(upload_to="brickset/boxes/", **BNULL)
|
||||
box_image_small = ImageSpecField(
|
||||
source="box_image",
|
||||
processors=[ResizeToFit(100, 100)],
|
||||
format="JPEG",
|
||||
options={"quality": 60},
|
||||
)
|
||||
box_image_medium = ImageSpecField(
|
||||
source="box_image",
|
||||
processors=[ResizeToFit(300, 300)],
|
||||
format="JPEG",
|
||||
options={"quality": 75},
|
||||
)
|
||||
set_image = models.ImageField(upload_to="brickset/sets/", **BNULL)
|
||||
set_image_small = ImageSpecField(
|
||||
source="set_image",
|
||||
processors=[ResizeToFit(100, 100)],
|
||||
format="JPEG",
|
||||
options={"quality": 60},
|
||||
)
|
||||
set_image_medium = ImageSpecField(
|
||||
source="set_image",
|
||||
processors=[ResizeToFit(300, 300)],
|
||||
format="JPEG",
|
||||
options={"quality": 75},
|
||||
)
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse("bricksets:brickset_detail", kwargs={"slug": self.uuid})
|
||||
|
||||
@property
|
||||
def logdata_cls(self):
|
||||
return BrickSetLogData
|
||||
|
||||
@classmethod
|
||||
def find_or_create(cls, title: str) -> "BrickSet":
|
||||
return cls.objects.filter(title=title).first()
|
||||
|
||||
@property
|
||||
def primary_image_url(self) -> str:
|
||||
if self.box_image:
|
||||
return self.box_image.url
|
||||
return ""
|
||||
14
vrobbler/apps/bricksets/urls.py
Normal file
14
vrobbler/apps/bricksets/urls.py
Normal file
@ -0,0 +1,14 @@
|
||||
from django.urls import path
|
||||
from bricksets import views
|
||||
|
||||
app_name = "bricksets"
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
path("bricksets/", views.BrickSetListView.as_view(), name="brickset_list"),
|
||||
path(
|
||||
"bricksets/<slug:slug>/",
|
||||
views.BrickSetDetailView.as_view(),
|
||||
name="brickset_detail",
|
||||
),
|
||||
]
|
||||
10
vrobbler/apps/bricksets/views.py
Normal file
10
vrobbler/apps/bricksets/views.py
Normal file
@ -0,0 +1,10 @@
|
||||
from bricksets.models import BrickSet
|
||||
from scrobbles.views import ScrobbleableListView, ScrobbleableDetailView
|
||||
|
||||
|
||||
class BrickSetListView(ScrobbleableListView):
|
||||
model = BrickSet
|
||||
|
||||
|
||||
class BrickSetDetailView(ScrobbleableDetailView):
|
||||
model = BrickSet
|
||||
@ -6,6 +6,7 @@ JELLYFIN_AUDIO_ITEM_TYPES = ["Audio"]
|
||||
LONG_PLAY_MEDIA = {
|
||||
"videogames": "VideoGame",
|
||||
"books": "Book",
|
||||
"bricksets": "BrickSet",
|
||||
}
|
||||
|
||||
PLAY_AGAIN_MEDIA = {
|
||||
@ -13,6 +14,7 @@ PLAY_AGAIN_MEDIA = {
|
||||
"books": "Book",
|
||||
"boardgames": "BoardGame",
|
||||
"moods": "Mood",
|
||||
"bricksets": "BrickSet",
|
||||
}
|
||||
|
||||
MEDIA_END_PADDING_SECONDS = {
|
||||
|
||||
@ -30,6 +30,9 @@ class JSONDataclass(JSONWizard):
|
||||
def json(self):
|
||||
return json.dumps(self.asdict)
|
||||
|
||||
class LongPlayLogData(JSONDataclass):
|
||||
serial_scrobble_id: Optional[int]
|
||||
long_play_complete: Optional[bool]
|
||||
|
||||
@dataclass
|
||||
class BoardGameScoreLogData(JSONDataclass):
|
||||
@ -69,7 +72,7 @@ class BoardGameScoreLogData(JSONDataclass):
|
||||
|
||||
|
||||
@dataclass
|
||||
class BoardGameLogData(JSONDataclass):
|
||||
class BoardGameLogData(LongPlayLogData):
|
||||
players: Optional[list[BoardGameScoreLogData]] = None
|
||||
location: Optional[str] = None
|
||||
geo_location_id: Optional[int] = None
|
||||
@ -92,10 +95,13 @@ class BookPageLogData(JSONDataclass):
|
||||
|
||||
|
||||
@dataclass
|
||||
class BookLogData(JSONDataclass):
|
||||
class BookLogData(LongPlayLogData):
|
||||
koreader_hash: Optional[str]
|
||||
pages_read: Optional[int]
|
||||
page_data: Optional[list[BookPageLogData]]
|
||||
page_end: Optional[int]
|
||||
page_start: Optional[int]
|
||||
serial_scrobble_id: Optional[int]
|
||||
|
||||
|
||||
@dataclass
|
||||
@ -148,7 +154,13 @@ class VideoLogData(JSONDataclass):
|
||||
|
||||
|
||||
@dataclass
|
||||
class VideoGameLogData(JSONDataclass):
|
||||
class VideoGameLogData(LongPlayLogData):
|
||||
emulated: bool = False
|
||||
console: Optional[str] = None
|
||||
emulator: Optional[str] = None
|
||||
|
||||
|
||||
@dataclass
|
||||
class BrickSetLogData(LongPlayLogData):
|
||||
built_with_names: Optional[list[str]]
|
||||
built_with_user_ids: Optional[list[str]]
|
||||
|
||||
25
vrobbler/apps/scrobbles/migrations/0060_scrobble_brickset.py
Normal file
25
vrobbler/apps/scrobbles/migrations/0060_scrobble_brickset.py
Normal file
@ -0,0 +1,25 @@
|
||||
# Generated by Django 4.2.15 on 2024-09-07 05:38
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("bricksets", "0001_initial"),
|
||||
("scrobbles", "0059_remove_scrobble_book_koreader_hash_and_more"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="scrobble",
|
||||
name="brickset",
|
||||
field=models.ForeignKey(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.DO_NOTHING,
|
||||
to="bricksets.brickset",
|
||||
),
|
||||
),
|
||||
]
|
||||
@ -0,0 +1,35 @@
|
||||
# Generated by Django 4.2.15 on 2024-09-07 05:54
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("scrobbles", "0060_scrobble_brickset"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name="scrobble",
|
||||
name="media_type",
|
||||
field=models.CharField(
|
||||
choices=[
|
||||
("Video", "Video"),
|
||||
("Track", "Track"),
|
||||
("PodcastEpisode", "Podcast episode"),
|
||||
("SportEvent", "Sport event"),
|
||||
("Book", "Book"),
|
||||
("VideoGame", "Video game"),
|
||||
("BoardGame", "Board game"),
|
||||
("GeoLocation", "GeoLocation"),
|
||||
("WebPage", "Web Page"),
|
||||
("LifeEvent", "Life event"),
|
||||
("Mood", "Mood"),
|
||||
("BrickSet", "Brick set"),
|
||||
],
|
||||
default="Video",
|
||||
max_length=14,
|
||||
),
|
||||
),
|
||||
]
|
||||
@ -96,6 +96,11 @@ class ScrobblableMixin(TimeStampedModel):
|
||||
def find_or_create(cls) -> None:
|
||||
logger.warning("find_or_create() not implemented yet")
|
||||
|
||||
def __str__(self):
|
||||
if self.title:
|
||||
return self.title
|
||||
return str(self.uuid)
|
||||
|
||||
|
||||
class LongPlayScrobblableMixin(ScrobblableMixin):
|
||||
class Meta:
|
||||
|
||||
@ -10,6 +10,7 @@ import pytz
|
||||
from boardgames.models import BoardGame
|
||||
from books.koreader import process_koreader_sqlite_file
|
||||
from books.models import Book
|
||||
from bricksets.models import BrickSet
|
||||
from django.conf import settings
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.db import models
|
||||
@ -485,6 +486,7 @@ class Scrobble(TimeStampedModel):
|
||||
WEBPAGE = "WebPage", "Web Page"
|
||||
LIFE_EVENT = "LifeEvent", "Life event"
|
||||
MOOD = "Mood", "Mood"
|
||||
BRICKSET = "BrickSet", "Brick set"
|
||||
|
||||
uuid = models.UUIDField(editable=False, **BNULL)
|
||||
video = models.ForeignKey(Video, on_delete=models.DO_NOTHING, **BNULL)
|
||||
@ -510,6 +512,7 @@ class Scrobble(TimeStampedModel):
|
||||
LifeEvent, on_delete=models.DO_NOTHING, **BNULL
|
||||
)
|
||||
mood = models.ForeignKey(Mood, on_delete=models.DO_NOTHING, **BNULL)
|
||||
brickset = models.ForeignKey(BrickSet, on_delete=models.DO_NOTHING, **BNULL)
|
||||
media_type = models.CharField(
|
||||
max_length=14, choices=MediaType.choices, default=MediaType.VIDEO
|
||||
)
|
||||
@ -881,6 +884,8 @@ class Scrobble(TimeStampedModel):
|
||||
media_obj = self.life_event
|
||||
if self.mood:
|
||||
media_obj = self.mood
|
||||
if self.brickset:
|
||||
media_obj = self.brickset
|
||||
return media_obj
|
||||
|
||||
def __str__(self):
|
||||
|
||||
@ -107,6 +107,7 @@ INSTALLED_APPS = [
|
||||
"sports",
|
||||
"books",
|
||||
"boardgames",
|
||||
"bricksets",
|
||||
"videogames",
|
||||
"locations",
|
||||
"webpages",
|
||||
|
||||
@ -121,6 +121,7 @@ INSTALLED_APPS = [
|
||||
"sports",
|
||||
"books",
|
||||
"boardgames",
|
||||
"bricksets",
|
||||
"videogames",
|
||||
"locations",
|
||||
"webpages",
|
||||
|
||||
@ -6,6 +6,7 @@ import vrobbler.apps.scrobbles.views as scrobbles_views
|
||||
from vrobbler.apps.books.api.views import AuthorViewSet, BookViewSet
|
||||
from vrobbler.apps.music import urls as music_urls
|
||||
from vrobbler.apps.books import urls as book_urls
|
||||
from vrobbler.apps.bricksets import urls as bricksets_urls
|
||||
from vrobbler.apps.sports import urls as sports_urls
|
||||
from vrobbler.apps.podcasts import urls as podcast_urls
|
||||
from vrobbler.apps.videogames import urls as videogame_urls
|
||||
@ -69,6 +70,7 @@ urlpatterns = [
|
||||
path("", include(video_urls, namespace="videos")),
|
||||
path("", include(videogame_urls, namespace="videogames")),
|
||||
path("", include(boardgame_urls, namespace="boardgames")),
|
||||
path("", include(bricksets_urls, namespace="bricksets")),
|
||||
path("", include(sports_urls, namespace="sports")),
|
||||
path("", include(locations_urls, namespace="locations")),
|
||||
path("", include(webpages_urls, namespace="webpages")),
|
||||
|
||||
Reference in New Issue
Block a user