Add boardgames as scrobblable
This commit is contained in:
30
vrobbler/apps/boardgames/admin.py
Normal file
30
vrobbler/apps/boardgames/admin.py
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
from django.contrib import admin
|
||||||
|
|
||||||
|
from boardgames.models import BoardGame, BoardGamePublisher
|
||||||
|
|
||||||
|
from scrobbles.admin import ScrobbleInline
|
||||||
|
|
||||||
|
|
||||||
|
@admin.register(BoardGamePublisher)
|
||||||
|
class BoardGamePublisherAdmin(admin.ModelAdmin):
|
||||||
|
date_hierarchy = "created"
|
||||||
|
list_display = (
|
||||||
|
"name",
|
||||||
|
"uuid",
|
||||||
|
)
|
||||||
|
ordering = ("-created",)
|
||||||
|
|
||||||
|
|
||||||
|
@admin.register(BoardGame)
|
||||||
|
class GameAdmin(admin.ModelAdmin):
|
||||||
|
date_hierarchy = "created"
|
||||||
|
list_display = (
|
||||||
|
"bggeek_id",
|
||||||
|
"title",
|
||||||
|
"published_date",
|
||||||
|
)
|
||||||
|
search_fields = ("title",)
|
||||||
|
ordering = ("-created",)
|
||||||
|
inlines = [
|
||||||
|
ScrobbleInline,
|
||||||
|
]
|
||||||
60
vrobbler/apps/boardgames/bgg.py
Normal file
60
vrobbler/apps/boardgames/bgg.py
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
import requests
|
||||||
|
from bs4 import BeautifulSoup
|
||||||
|
|
||||||
|
SEARCH_ID_URL = "https://boardgamegeek.com/xmlapi/search?search={query}"
|
||||||
|
GAME_ID_URL = "https://boardgamegeek.com/xmlapi/boardgame/{id}"
|
||||||
|
|
||||||
|
|
||||||
|
def take_first(thing: Optional[list]) -> str:
|
||||||
|
first = ""
|
||||||
|
try:
|
||||||
|
first = thing[0]
|
||||||
|
except IndexError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if first:
|
||||||
|
first = first.get_text()
|
||||||
|
|
||||||
|
return first
|
||||||
|
|
||||||
|
|
||||||
|
def get_id_from_bgg(title):
|
||||||
|
soup = ""
|
||||||
|
headers = {"User-Agent": "Vrobbler 0.11.12"}
|
||||||
|
url = SEARCH_ID_URL.format(query=title)
|
||||||
|
r = requests.get(url, headers=headers)
|
||||||
|
if r.status_code == 200:
|
||||||
|
soup = BeautifulSoup(r.text, "xml")
|
||||||
|
return soup
|
||||||
|
|
||||||
|
|
||||||
|
def get_game_by_id_from_bgg(game_id):
|
||||||
|
soup = None
|
||||||
|
game_dict = {}
|
||||||
|
headers = {"User-Agent": "Vrobbler 0.11.12"}
|
||||||
|
url = GAME_ID_URL.format(id=game_id)
|
||||||
|
r = requests.get(url, headers=headers)
|
||||||
|
if r.status_code == 200:
|
||||||
|
soup = BeautifulSoup(r.text, "xml")
|
||||||
|
|
||||||
|
if soup:
|
||||||
|
seconds_to_play = None
|
||||||
|
minutes = take_first(soup.findAll("playingtime"))
|
||||||
|
if minutes:
|
||||||
|
seconds_to_play = int(minutes) * 60
|
||||||
|
|
||||||
|
game_dict = {
|
||||||
|
"title": take_first(soup.findAll("name", primary="true")),
|
||||||
|
"description": take_first(soup.findAll("description")),
|
||||||
|
"year_published": take_first(soup.findAll("yearpublished")),
|
||||||
|
"publisher_name": take_first(soup.findAll("boardgamepublisher")),
|
||||||
|
"cover_url": take_first(soup.findAll("image")),
|
||||||
|
"min_players": take_first(soup.findAll("minplayers")),
|
||||||
|
"max_players": take_first(soup.findAll("maxplayers")),
|
||||||
|
"recommended_age": take_first(soup.findAll("age")),
|
||||||
|
"run_time_seconds": seconds_to_play,
|
||||||
|
}
|
||||||
|
|
||||||
|
return game_dict
|
||||||
168
vrobbler/apps/boardgames/migrations/0001_initial.py
Normal file
168
vrobbler/apps/boardgames/migrations/0001_initial.py
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
# Generated by Django 4.1.7 on 2023-04-17 22:11
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
import django_extensions.db.fields
|
||||||
|
import taggit.managers
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("scrobbles", "0038_alter_objectwithgenres_tag"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name="BoardGamePublisher",
|
||||||
|
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"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
("name", models.CharField(max_length=255)),
|
||||||
|
(
|
||||||
|
"uuid",
|
||||||
|
models.UUIDField(
|
||||||
|
blank=True,
|
||||||
|
default=uuid.uuid4,
|
||||||
|
editable=False,
|
||||||
|
null=True,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"logo",
|
||||||
|
models.ImageField(
|
||||||
|
blank=True,
|
||||||
|
null=True,
|
||||||
|
upload_to="games/platform-logos/",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
("igdb_id", models.IntegerField(blank=True, null=True)),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
"get_latest_by": "modified",
|
||||||
|
"abstract": False,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name="BoardGame",
|
||||||
|
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"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"run_time_seconds",
|
||||||
|
models.IntegerField(blank=True, null=True),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"run_time_ticks",
|
||||||
|
models.PositiveBigIntegerField(blank=True, null=True),
|
||||||
|
),
|
||||||
|
("title", models.CharField(max_length=255)),
|
||||||
|
(
|
||||||
|
"uuid",
|
||||||
|
models.UUIDField(
|
||||||
|
blank=True,
|
||||||
|
default=uuid.uuid4,
|
||||||
|
editable=False,
|
||||||
|
null=True,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"cover",
|
||||||
|
models.ImageField(
|
||||||
|
blank=True, null=True, upload_to="boardgames/covers/"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"layout_image",
|
||||||
|
models.ImageField(
|
||||||
|
blank=True, null=True, upload_to="boardgames/layouts/"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
("summary", models.TextField(blank=True, null=True)),
|
||||||
|
("rating", models.FloatField(blank=True, null=True)),
|
||||||
|
(
|
||||||
|
"max_players",
|
||||||
|
models.PositiveSmallIntegerField(blank=True, null=True),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"min_players",
|
||||||
|
models.PositiveSmallIntegerField(blank=True, null=True),
|
||||||
|
),
|
||||||
|
("published_date", models.DateField(blank=True, null=True)),
|
||||||
|
(
|
||||||
|
"recommened_age",
|
||||||
|
models.PositiveSmallIntegerField(blank=True, null=True),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"seconds_to_play",
|
||||||
|
models.IntegerField(blank=True, null=True),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"bggeek_id",
|
||||||
|
models.CharField(blank=True, max_length=255, null=True),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"genre",
|
||||||
|
taggit.managers.TaggableManager(
|
||||||
|
help_text="A comma-separated list of tags.",
|
||||||
|
through="scrobbles.ObjectWithGenres",
|
||||||
|
to="scrobbles.Genre",
|
||||||
|
verbose_name="Tags",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"publisher",
|
||||||
|
models.ForeignKey(
|
||||||
|
blank=True,
|
||||||
|
null=True,
|
||||||
|
on_delete=django.db.models.deletion.DO_NOTHING,
|
||||||
|
to="boardgames.boardgamepublisher",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
"abstract": False,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 4.1.7 on 2023-04-17 22:16
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("boardgames", "0001_initial"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="boardgame",
|
||||||
|
name="description",
|
||||||
|
field=models.TextField(blank=True, null=True),
|
||||||
|
),
|
||||||
|
]
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 4.1.7 on 2023-04-17 22:17
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("boardgames", "0002_boardgame_description"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RenameField(
|
||||||
|
model_name="boardgame",
|
||||||
|
old_name="recommened_age",
|
||||||
|
new_name="recommended_age",
|
||||||
|
),
|
||||||
|
]
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
# Generated by Django 4.1.7 on 2023-04-17 22:25
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("boardgames", "0003_rename_recommened_age_boardgame_recommended_age"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name="boardgame",
|
||||||
|
name="seconds_to_play",
|
||||||
|
),
|
||||||
|
]
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
# Generated by Django 4.1.7 on 2023-04-17 22:29
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("boardgames", "0004_remove_boardgame_seconds_to_play"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name="boardgame",
|
||||||
|
name="summary",
|
||||||
|
),
|
||||||
|
]
|
||||||
0
vrobbler/apps/boardgames/migrations/__init__.py
Normal file
0
vrobbler/apps/boardgames/migrations/__init__.py
Normal file
128
vrobbler/apps/boardgames/models.py
Normal file
128
vrobbler/apps/boardgames/models.py
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
import logging
|
||||||
|
from datetime import datetime
|
||||||
|
from typing import Optional
|
||||||
|
from uuid import uuid4
|
||||||
|
|
||||||
|
import requests
|
||||||
|
from boardgames.bgg import get_game_by_id_from_bgg
|
||||||
|
from django.conf import settings
|
||||||
|
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 scrobbles.mixins import ScrobblableMixin
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
BNULL = {"blank": True, "null": True}
|
||||||
|
|
||||||
|
|
||||||
|
class BoardGamePublisher(TimeStampedModel):
|
||||||
|
name = models.CharField(max_length=255)
|
||||||
|
uuid = models.UUIDField(default=uuid4, editable=False, **BNULL)
|
||||||
|
logo = models.ImageField(upload_to="games/platform-logos/", **BNULL)
|
||||||
|
igdb_id = models.IntegerField(**BNULL)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
def get_absolute_url(self):
|
||||||
|
return reverse(
|
||||||
|
"boardgames:publisher_detail", kwargs={"slug": self.uuid}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class BoardGame(ScrobblableMixin):
|
||||||
|
COMPLETION_PERCENT = getattr(
|
||||||
|
settings, "BOARD_GAME_COMPLETION_PERCENT", 100
|
||||||
|
)
|
||||||
|
|
||||||
|
FIELDS_FROM_BGGEEK = [
|
||||||
|
"igdb_id",
|
||||||
|
"alternative_name",
|
||||||
|
"rating",
|
||||||
|
"rating_count",
|
||||||
|
"release_date",
|
||||||
|
"cover",
|
||||||
|
"screenshot",
|
||||||
|
]
|
||||||
|
|
||||||
|
title = models.CharField(max_length=255)
|
||||||
|
publisher = models.ForeignKey(
|
||||||
|
BoardGamePublisher, **BNULL, on_delete=models.DO_NOTHING
|
||||||
|
)
|
||||||
|
uuid = models.UUIDField(default=uuid4, editable=False, **BNULL)
|
||||||
|
description = models.TextField(**BNULL)
|
||||||
|
cover = models.ImageField(upload_to="boardgames/covers/", **BNULL)
|
||||||
|
layout_image = models.ImageField(upload_to="boardgames/layouts/", **BNULL)
|
||||||
|
rating = models.FloatField(**BNULL)
|
||||||
|
max_players = models.PositiveSmallIntegerField(**BNULL)
|
||||||
|
min_players = models.PositiveSmallIntegerField(**BNULL)
|
||||||
|
published_date = models.DateField(**BNULL)
|
||||||
|
recommended_age = models.PositiveSmallIntegerField(**BNULL)
|
||||||
|
bggeek_id = models.CharField(max_length=255, **BNULL)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.title
|
||||||
|
|
||||||
|
def get_absolute_url(self):
|
||||||
|
return reverse(
|
||||||
|
"boardgames:boardgame_detail", kwargs={"slug": self.uuid}
|
||||||
|
)
|
||||||
|
|
||||||
|
def bggeek_link(self):
|
||||||
|
link = ""
|
||||||
|
if self.bggeek_id:
|
||||||
|
link = f"https://boardgamegeek.com/boardgame/{self.bggeek_id}"
|
||||||
|
return link
|
||||||
|
|
||||||
|
def fix_metadata(self, data: dict = {}, force_update=False) -> None:
|
||||||
|
|
||||||
|
if not self.bggeek_id or force_update:
|
||||||
|
|
||||||
|
if not data:
|
||||||
|
data = get_game_by_id_from_bgg(self.bggeek_id)
|
||||||
|
|
||||||
|
cover_url = data.pop("cover_url")
|
||||||
|
year = data.pop("year_published")
|
||||||
|
publisher_name = data.pop("publisher_name")
|
||||||
|
|
||||||
|
if year:
|
||||||
|
data["published_date"] = datetime(int(year), 1, 1)
|
||||||
|
|
||||||
|
# Fun trick for updating all fields at once
|
||||||
|
BoardGame.objects.filter(pk=self.id).update(**data)
|
||||||
|
self.refresh_from_db()
|
||||||
|
|
||||||
|
# Add publishers
|
||||||
|
(
|
||||||
|
self.publisher,
|
||||||
|
_created,
|
||||||
|
) = BoardGamePublisher.objects.get_or_create(name=publisher_name)
|
||||||
|
self.save()
|
||||||
|
|
||||||
|
# Go get cover image if the URL is present
|
||||||
|
if cover_url and not self.cover:
|
||||||
|
headers = {"User-Agent": "Vrobbler 0.11.12"}
|
||||||
|
r = requests.get(cover_url, headers=headers)
|
||||||
|
logger.debug(r.status_code)
|
||||||
|
if r.status_code == 200:
|
||||||
|
fname = f"{self.title}_cover_{self.uuid}.jpg"
|
||||||
|
self.cover.save(fname, ContentFile(r.content), save=True)
|
||||||
|
logger.debug("Loaded cover image from BGGeek")
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def find_or_create(cls, lookup_id: str) -> Optional["BoardGame"]:
|
||||||
|
"""Given a Lookup ID (either BGG or BGA ID), return a board game object"""
|
||||||
|
data = {}
|
||||||
|
boardgame = None
|
||||||
|
|
||||||
|
data = get_game_by_id_from_bgg(lookup_id)
|
||||||
|
|
||||||
|
if data:
|
||||||
|
boardgame, created = cls.objects.get_or_create(
|
||||||
|
title=data["title"], bggeek_id=lookup_id
|
||||||
|
)
|
||||||
|
if created:
|
||||||
|
boardgame.fix_metadata(data=data)
|
||||||
|
|
||||||
|
return boardgame
|
||||||
0
vrobbler/apps/boardgames/utils.py
Normal file
0
vrobbler/apps/boardgames/utils.py
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
# Generated by Django 4.1.7 on 2023-04-17 22:01
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("scrobbles", "0037_scrobble_media_type"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="objectwithgenres",
|
||||||
|
name="tag",
|
||||||
|
field=models.ForeignKey(
|
||||||
|
blank=True,
|
||||||
|
null=True,
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
related_name="%(app_label)s_%(class)s_items",
|
||||||
|
to="scrobbles.genre",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
# Generated by Django 4.1.7 on 2023-04-17 22:11
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("boardgames", "0001_initial"),
|
||||||
|
("scrobbles", "0038_alter_objectwithgenres_tag"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="scrobble",
|
||||||
|
name="board_game",
|
||||||
|
field=models.ForeignKey(
|
||||||
|
blank=True,
|
||||||
|
null=True,
|
||||||
|
on_delete=django.db.models.deletion.DO_NOTHING,
|
||||||
|
to="boardgames.boardgame",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
||||||
@ -27,6 +27,7 @@ class ObjectWithGenres(GenericTaggedItemBase):
|
|||||||
Genre,
|
Genre,
|
||||||
on_delete=models.CASCADE,
|
on_delete=models.CASCADE,
|
||||||
related_name="%(app_label)s_%(class)s_items",
|
related_name="%(app_label)s_%(class)s_items",
|
||||||
|
**BNULL,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import logging
|
|||||||
from typing import Optional
|
from typing import Optional
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
|
|
||||||
|
from boardgames.models import BoardGame
|
||||||
from books.models import Book
|
from books.models import Book
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
@ -434,6 +435,9 @@ class Scrobble(TimeStampedModel):
|
|||||||
video_game = models.ForeignKey(
|
video_game = models.ForeignKey(
|
||||||
VideoGame, on_delete=models.DO_NOTHING, **BNULL
|
VideoGame, on_delete=models.DO_NOTHING, **BNULL
|
||||||
)
|
)
|
||||||
|
board_game = models.ForeignKey(
|
||||||
|
BoardGame, on_delete=models.DO_NOTHING, **BNULL
|
||||||
|
)
|
||||||
media_type = models.CharField(
|
media_type = models.CharField(
|
||||||
max_length=14, choices=MediaType.choices, default=MediaType.VIDEO
|
max_length=14, choices=MediaType.choices, default=MediaType.VIDEO
|
||||||
)
|
)
|
||||||
@ -583,6 +587,8 @@ class Scrobble(TimeStampedModel):
|
|||||||
media_obj = self.book
|
media_obj = self.book
|
||||||
if self.video_game:
|
if self.video_game:
|
||||||
media_obj = self.video_game
|
media_obj = self.video_game
|
||||||
|
if self.board_game:
|
||||||
|
media_obj = self.board_game
|
||||||
return media_obj
|
return media_obj
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
@ -614,6 +620,9 @@ class Scrobble(TimeStampedModel):
|
|||||||
if media_class == "VideoGame":
|
if media_class == "VideoGame":
|
||||||
media_query = models.Q(video_game=media)
|
media_query = models.Q(video_game=media)
|
||||||
scrobble_data["video_game_id"] = media.id
|
scrobble_data["video_game_id"] = media.id
|
||||||
|
if media_class == "BoardGame":
|
||||||
|
media_query = models.Q(board_game=media)
|
||||||
|
scrobble_data["board_game_id"] = media.id
|
||||||
|
|
||||||
scrobble = (
|
scrobble = (
|
||||||
cls.objects.filter(
|
cls.objects.filter(
|
||||||
|
|||||||
@ -112,6 +112,7 @@ INSTALLED_APPS = [
|
|||||||
"podcasts",
|
"podcasts",
|
||||||
"sports",
|
"sports",
|
||||||
"books",
|
"books",
|
||||||
|
"boardgames",
|
||||||
"videogames",
|
"videogames",
|
||||||
"mathfilters",
|
"mathfilters",
|
||||||
"rest_framework",
|
"rest_framework",
|
||||||
|
|||||||
Reference in New Issue
Block a user