Add board game urls and views

This commit is contained in:
2023-04-18 11:04:02 -04:00
parent 5c3a554010
commit fc927c72d1
7 changed files with 244 additions and 10 deletions

View File

@ -3,7 +3,9 @@ from typing import Optional
import requests
from bs4 import BeautifulSoup
SEARCH_ID_URL = "https://boardgamegeek.com/xmlapi/search?search={query}"
SEARCH_ID_URL = (
"https://boardgamegeek.com/xmlapi/search?search={query}&exact=1"
)
GAME_ID_URL = "https://boardgamegeek.com/xmlapi/boardgame/{id}"
@ -20,21 +22,41 @@ def take_first(thing: Optional[list]) -> str:
return first
def get_id_from_bgg(title):
soup = ""
def lookup_boardgame_id_from_bgg(title: str) -> Optional[int]:
soup = None
headers = {"User-Agent": "Vrobbler 0.11.12"}
game_id = None
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
if soup:
result = soup.findAll("boardgame")
if not result:
return game_id
game_id = result[0].get("objectid", None)
return game_id
def get_game_by_id_from_bgg(game_id):
def lookup_boardgame_from_bgg(lookup_id: str) -> dict:
soup = None
game_dict = {}
headers = {"User-Agent": "Vrobbler 0.11.12"}
url = GAME_ID_URL.format(id=game_id)
title = ""
bgg_id = None
try:
bgg_id = int(lookup_id)
except ValueError:
title = lookup_id
if not bgg_id:
bgg_id = lookup_boardgame_id_from_bgg(title)
url = GAME_ID_URL.format(id=bgg_id)
r = requests.get(url, headers=headers)
if r.status_code == 200:
soup = BeautifulSoup(r.text, "xml")

View File

@ -4,7 +4,7 @@ from typing import Optional
from uuid import uuid4
import requests
from boardgames.bgg import get_game_by_id_from_bgg
from boardgames.bgg import lookup_boardgame_from_bgg
from django.conf import settings
from django.core.files.base import ContentFile
from django.db import models
@ -69,6 +69,12 @@ class BoardGame(ScrobblableMixin):
"boardgames:boardgame_detail", kwargs={"slug": self.uuid}
)
def primary_image_url(self) -> str:
url = ""
if self.cover:
url = self.cover.url
return url
def bggeek_link(self):
link = ""
if self.bggeek_id:
@ -111,12 +117,14 @@ class BoardGame(ScrobblableMixin):
logger.debug("Loaded cover image from BGGeek")
@classmethod
def find_or_create(cls, lookup_id: str) -> Optional["BoardGame"]:
def find_or_create(
cls, lookup_id: str, data: Optional[dict] = {}
) -> 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 not data:
data = lookup_boardgame_from_bgg(lookup_id)
if data:
boardgame, created = cls.objects.get_or_create(

View File

@ -0,0 +1,21 @@
from django.urls import path
from boardgames import views
app_name = "boardgames"
urlpatterns = [
path(
"board-game/", views.BoardGameListView.as_view(), name="boardgame_list"
),
path(
"board-game/<slug:slug>/",
views.BoardGameDetailView.as_view(),
name="boardgame_detail",
),
path(
"board-game-publisher/<slug:slug>/",
views.BoardGamePublisherDetailView.as_view(),
name="publisher_detail",
),
]

View File

@ -0,0 +1,17 @@
from django.views import generic
from boardgames.models import BoardGame, BoardGamePublisher
class BoardGameListView(generic.ListView):
model = BoardGame
paginate_by = 20
class BoardGameDetailView(generic.DetailView):
model = BoardGame
slug_field = "uuid"
class BoardGamePublisherDetailView(generic.DetailView):
model = BoardGamePublisher
slug_field = "uuid"

View File

@ -0,0 +1,82 @@
{% extends "base_list.html" %}
{% load mathfilters %}
{% load static %}
{% load naturalduration %}
{% block title %}{{object.title}}{% endblock %}
{% block head_extra %}
<style>
.cover img {
width: 250px;
}
.cover {
float: left;
width: 252px;
padding: 0;
}
.summary {
float: left;
width: 600px;
margin-left: 10px;
}
</style>
{% endblock %}
{% block lists %}
<div class="row">
{% if object.cover %}
<div class="cover"><img src="{{object.cover.url}}" /></div>
{% endif %}
<div class="summary">
{% if object.description%}
<p>{{object.description|safe|linebreaks|truncatewords:160}}</p>
<hr />
{% endif %}
<p style="float:right;">
<a href="{{object.bggeek_link}}"><img src="{% static "images/bgg-logo.png" %}" width=35></a>
</p>
</div>
</div>
<div class="row">
<p>{{object.scrobble_set.count}} scrobbles</p>
<p>{{object.scrobble_set.last.long_play_seconds|natural_duration}}{% if object.scrobble_set.last.long_play_complete %} and completed{% else %} spent playing{% endif %}</p>
<p>
{% if object.scrobble_set.last.long_play_complete == True %}
<a href="">Play again</a>
{% else %}
<a href="{{object.get_start_url}}">Resume playing</a>
{% endif %}
</p>
</div>
<div class="row">
<div class="col-md">
<h3>Last scrobbles</h3>
<div class="table-responsive">
<table class="table table-striped table-sm">
<thead>
<tr>
<th scope="col">Date</th>
<th scope="col">Completed</th>
<th scope="col">Duration</th>
<th scope="col">Platforms</th>
</tr>
</thead>
<tbody>
{% for scrobble in object.scrobble_set.all|dictsortreversed:"timestamp" %}
<tr>
<td>{{scrobble.timestamp}}</td>
<td>{% if scrobble.long_play_complete == True %}Yes{% endif %}</td>
<td>{% if scrobble.in_progress %}Now playing{% else %}{{scrobble.playback_position_seconds|natural_duration}}{% endif %}</td>
<td>{% for platform in scrobble.video_game.platforms.all %}<a href="{{platform.get_absolute_url}}">{{platform}}</a>{% if not forloop.last %}, {% endif %}{% endfor %}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,82 @@
{% extends "base_list.html" %}
{% load urlreplace %}
{% block title %}Albums{% endblock %}
{% block lists %}
<div class="row">
<p class="view">
<span class="view-links">
{% if view == 'grid' %}
View as <a href="?{% urlreplace view='list' %}">List</a>
{% else %}
View as <a href="?{% urlreplace view='grid' %}">Grid</a>
{% endif %}
</span>
</p>
<p class="pagination">
<span class="page-links">
{% if page_obj.has_previous %}
<a href="?{% urlreplace page=page_obj.previous_page_number %}">prev</a>
{% endif %}
<span class="page-current">
Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}
</span>
{% if page_obj.has_next %}
<a href="?{% urlreplace page=page_obj.next_page_number %}">next</a>
{% endif %}
</span>
</p>
<hr />
{% if view == 'grid' %}
<div>
{% for game in object_list %}
{% if game.cover %}
<dl style="width: 130px; float: left; margin-right:10px;">
<dd><img src="{{game.cover.url}}" width=120 height=120 /></dd>
</dl>
{% endif %}
{% endfor %}
</div>
{% else %}
<div class="col-md">
<div class="table-responsive">
<table class="table table-striped table-sm">
<thead>
<tr>
<th scope="col">Scrobbles</th>
<th scope="col">Name</th>
<th scope="col">Publisher</th>
</tr>
</thead>
<tbody>
{% for game in object_list %}
<tr>
<td>{{game.scrobbles.count}}</td>
<td><a href="{{game.get_absolute_url}}">{{game}}</a></td>
<td><a href="{{game.publisher.get_absolute_url}}">{{game.publisher}}</a></td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endif %}
<div class="pagination" style="margin-bottom:50px;">
<span class="page-links">
{% if page_obj.has_previous %}
<a href="?{% urlreplace page=page_obj.previous_page_number %}">prev</a>
{% endif %}
<span class="page-current">
Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}
</span>
{% if page_obj.has_next %}
<a href="?{% urlreplace page=page_obj.next_page_number %}">next</a>
{% endif %}
</span>
</div>
</div>
{% endblock %}

View File

@ -9,6 +9,7 @@ from vrobbler.apps.books import urls as book_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
from vrobbler.apps.boardgames import urls as boardgame_urls
from vrobbler.apps.music.api.views import (
AlbumViewSet,
ArtistViewSet,
@ -63,6 +64,7 @@ urlpatterns = [
path("", include(book_urls, namespace="books")),
path("", include(video_urls, namespace="videos")),
path("", include(videogame_urls, namespace="videogames")),
path("", include(boardgame_urls, namespace="boardgames")),
path("", include(sports_urls, namespace="sports")),
path("", include(podcast_urls, namespace="podcasts")),
path("", include(scrobble_urls, namespace="scrobbles")),