[templates] Clean up widgets
This commit is contained in:
@ -1,30 +1,25 @@
|
|||||||
from django.urls import path
|
from django.urls import path
|
||||||
from scrobbles import views
|
from scrobbles import views
|
||||||
from tasks.webhooks import todoist_webhook, emacs_webhook
|
from tasks.webhooks import emacs_webhook, todoist_webhook
|
||||||
|
|
||||||
app_name = "scrobbles"
|
app_name = "scrobbles"
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path("status/", views.ScrobbleStatusView.as_view(), name="status"),
|
path("status/", views.ScrobbleStatusView.as_view(), name="status"),
|
||||||
path(
|
path(
|
||||||
"widget/top-artists/<int:user_id>/",
|
"widget/top-artists/<str:period>/<int:user_id>/",
|
||||||
views.EmbeddableTopArtistWidget.as_view(),
|
views.EmbeddableTopArtistsWidget.as_view(),
|
||||||
name="embeddable-top-artists",
|
name="embeddable-top-artists",
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
"widget/top-board-games/month/<int:user_id>/",
|
"widget/top-board-games/<str:period>/<int:user_id>/",
|
||||||
views.EmbeddableTopBoardGamesMonthWidget.as_view(),
|
views.EmbeddableTopBoardGamesWidget.as_view(),
|
||||||
name="embeddable-top-board-games-month",
|
name="embeddable-top-board-games",
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
"widget/top-board-games/week/<int:user_id>/",
|
"widget/top-books/<str:period>/<int:user_id>/",
|
||||||
views.EmbeddableTopBoardGamesWeekWidget.as_view(),
|
views.EmbeddableTopBooksWidget.as_view(),
|
||||||
name="embeddable-top-board-games-week",
|
name="embeddable-top-books",
|
||||||
),
|
|
||||||
path(
|
|
||||||
"widget/top-board-games/year/<int:user_id>/",
|
|
||||||
views.EmbeddableTopBoardGamesYearWidget.as_view(),
|
|
||||||
name="embeddable-top-board-games-year",
|
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
"manual/lookup/",
|
"manual/lookup/",
|
||||||
|
|||||||
@ -8,12 +8,17 @@ import pytz
|
|||||||
from dateutil.relativedelta import relativedelta
|
from dateutil.relativedelta import relativedelta
|
||||||
from django.apps import apps
|
from django.apps import apps
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
from django.core.paginator import EmptyPage, PageNotAnInteger, Paginator
|
from django.core.paginator import EmptyPage, PageNotAnInteger, Paginator
|
||||||
from django.db.models import Count, Max, Q
|
from django.db.models import Count, Max, Q
|
||||||
from django.db.models.query import QuerySet
|
from django.db.models.query import QuerySet
|
||||||
from django.http import FileResponse, Http404, HttpResponseRedirect, JsonResponse
|
from django.http import (
|
||||||
|
FileResponse,
|
||||||
|
Http404,
|
||||||
|
HttpResponseRedirect,
|
||||||
|
JsonResponse,
|
||||||
|
)
|
||||||
from django.shortcuts import redirect
|
from django.shortcuts import redirect
|
||||||
from django.urls import reverse_lazy
|
from django.urls import reverse_lazy
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
@ -24,13 +29,15 @@ from django.views.generic.list import ListView
|
|||||||
from moods.models import Mood
|
from moods.models import Mood
|
||||||
from music.aggregators import (
|
from music.aggregators import (
|
||||||
artist_scrobble_count,
|
artist_scrobble_count,
|
||||||
week_of_scrobbles,
|
|
||||||
scrobble_counts,
|
|
||||||
live_charts,
|
live_charts,
|
||||||
live_tv_charts,
|
live_tv_charts,
|
||||||
live_youtube_channel_charts,
|
live_youtube_channel_charts,
|
||||||
|
scrobble_counts,
|
||||||
|
week_of_scrobbles,
|
||||||
)
|
)
|
||||||
from pendulum.parsing.exceptions import ParserError
|
from pendulum.parsing.exceptions import ParserError
|
||||||
|
from profiles.models import UserProfile
|
||||||
|
from profiles.utils import now_user_timezone
|
||||||
from rest_framework import status
|
from rest_framework import status
|
||||||
from rest_framework.decorators import (
|
from rest_framework.decorators import (
|
||||||
api_view,
|
api_view,
|
||||||
@ -67,8 +74,6 @@ from scrobbles.utils import (
|
|||||||
get_long_plays_completed,
|
get_long_plays_completed,
|
||||||
get_long_plays_in_progress,
|
get_long_plays_in_progress,
|
||||||
)
|
)
|
||||||
from profiles.models import UserProfile
|
|
||||||
from profiles.utils import now_user_timezone
|
|
||||||
|
|
||||||
User = get_user_model()
|
User = get_user_model()
|
||||||
|
|
||||||
@ -1019,170 +1024,156 @@ class ScrobbleDetailView(DetailView):
|
|||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
class EmbeddableTopArtistWidget(TemplateView):
|
class BaseEmbeddableWidget(TemplateView):
|
||||||
template_name = "scrobbles/embeddable_top_artist.html"
|
template_name = "scrobbles/embeddable_top_media.html"
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
media_type = "Media"
|
||||||
context = super().get_context_data(**kwargs)
|
count_label = "plays"
|
||||||
|
no_data_message = "No scrobbles"
|
||||||
|
model = None
|
||||||
|
scrobble_filter = {}
|
||||||
|
|
||||||
|
def get_user_and_profile(self):
|
||||||
user_id = self.kwargs.get("user_id")
|
user_id = self.kwargs.get("user_id")
|
||||||
|
user = User.objects.get(id=user_id)
|
||||||
try:
|
|
||||||
user = User.objects.get(id=user_id)
|
|
||||||
except User.DoesNotExist:
|
|
||||||
context["error"] = "User not found"
|
|
||||||
return context
|
|
||||||
|
|
||||||
profile = user.profile
|
profile = user.profile
|
||||||
if not profile.enable_public_widgets:
|
if not profile.enable_public_widgets:
|
||||||
raise Http404("Public widgets are not enabled for this user")
|
raise Http404("Public widgets are not enabled for this user")
|
||||||
|
return user, profile
|
||||||
|
|
||||||
|
def get_date_range(self, user):
|
||||||
|
now = timezone.now()
|
||||||
|
if user.is_authenticated:
|
||||||
|
now = now_user_timezone(user.profile)
|
||||||
|
now = now.date()
|
||||||
|
|
||||||
|
period = self.kwargs.get("period", "week")
|
||||||
|
if period == "month":
|
||||||
|
start = now.replace(day=1)
|
||||||
|
label = now.strftime("%B %Y")
|
||||||
|
elif period == "year":
|
||||||
|
start = now.replace(month=1, day=1)
|
||||||
|
label = now.strftime("%Y")
|
||||||
|
else:
|
||||||
|
start = now - timedelta(days=now.isoweekday() % 7)
|
||||||
|
label = "This Week"
|
||||||
|
|
||||||
|
return start, label
|
||||||
|
|
||||||
|
def get_items(self, user, start_date, model=None):
|
||||||
|
from django.db.models import Count
|
||||||
|
|
||||||
|
model = model or self.model
|
||||||
|
queryset = (
|
||||||
|
model.objects.filter(
|
||||||
|
scrobble__user=user,
|
||||||
|
scrobble__timestamp__gte=start_date,
|
||||||
|
**self.scrobble_filter,
|
||||||
|
)
|
||||||
|
.annotate(count=Count("scrobble", distinct=True))
|
||||||
|
.order_by("-count")[:10]
|
||||||
|
)
|
||||||
|
|
||||||
|
items = list(queryset)
|
||||||
|
for item in items:
|
||||||
|
if not hasattr(item, "name") and hasattr(item, "title"):
|
||||||
|
item.name = item.title
|
||||||
|
return items
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = super().get_context_data(**kwargs)
|
||||||
|
user, profile = self.get_user_and_profile()
|
||||||
|
|
||||||
context["user"] = user
|
context["user"] = user
|
||||||
context["custom_css"] = profile.widget_custom_css
|
context["custom_css"] = profile.widget_custom_css
|
||||||
|
context["media_type"] = self.media_type
|
||||||
|
context["count_label"] = self.count_label
|
||||||
|
|
||||||
artist = {
|
start_date, period_label = self.get_date_range(user)
|
||||||
"user": user,
|
context["period_label"] = period_label
|
||||||
"media_type": "Artist",
|
|
||||||
"limit": 10,
|
items = self.get_items(user, start_date)
|
||||||
}
|
context["items"] = items
|
||||||
context["top_artists"] = list(live_charts(**artist, chart_period="last7"))
|
context["no_data_message"] = self.no_data_message
|
||||||
|
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
class EmbeddableTopBoardGamesMonthWidget(TemplateView):
|
class EmbeddableTopArtistsWidget(TemplateView):
|
||||||
template_name = "scrobbles/embeddable_top_board_games_month.html"
|
template_name = "scrobbles/embeddable_top_media.html"
|
||||||
|
media_type = "Artists"
|
||||||
|
count_label = "scrobbles"
|
||||||
|
no_data_message = "No scrobbles"
|
||||||
|
|
||||||
|
def get_user_and_profile(self):
|
||||||
|
user_id = self.kwargs.get("user_id")
|
||||||
|
user = User.objects.get(id=user_id)
|
||||||
|
profile = user.profile
|
||||||
|
if not profile.enable_public_widgets:
|
||||||
|
raise Http404("Public widgets are not enabled for this user")
|
||||||
|
return user, profile
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
from django.db.models import Count
|
context = super().get_context_data(**kwargs)
|
||||||
from django.utils import timezone
|
user, profile = self.get_user_and_profile()
|
||||||
|
|
||||||
|
context["user"] = user
|
||||||
|
context["custom_css"] = profile.widget_custom_css
|
||||||
|
context["media_type"] = self.media_type
|
||||||
|
context["count_label"] = self.count_label
|
||||||
|
context["no_data_message"] = self.no_data_message
|
||||||
|
|
||||||
|
period = self.kwargs.get("period", "week")
|
||||||
|
period_map = {"week": "last7", "month": "last30", "year": "year"}
|
||||||
|
chart_period = period_map.get(period, "last7")
|
||||||
|
|
||||||
|
artist = {"user": user, "media_type": "Artist", "limit": 10}
|
||||||
|
items = list(live_charts(**artist, chart_period=chart_period))
|
||||||
|
|
||||||
|
for item in items:
|
||||||
|
item.count = item.num_scrobbles
|
||||||
|
|
||||||
|
context["items"] = items
|
||||||
|
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from boardgames.models import BoardGame
|
|
||||||
|
|
||||||
context = super().get_context_data(**kwargs)
|
|
||||||
user_id = self.kwargs.get("user_id")
|
|
||||||
|
|
||||||
try:
|
|
||||||
user = User.objects.get(id=user_id)
|
|
||||||
except User.DoesNotExist:
|
|
||||||
context["error"] = "User not found"
|
|
||||||
return context
|
|
||||||
|
|
||||||
profile = user.profile
|
|
||||||
if not profile.enable_public_widgets:
|
|
||||||
raise Http404("Public widgets are not enabled for this user")
|
|
||||||
|
|
||||||
context["user"] = user
|
|
||||||
context["custom_css"] = profile.widget_custom_css
|
|
||||||
|
|
||||||
now = timezone.now()
|
|
||||||
if user.is_authenticated:
|
|
||||||
now = now_user_timezone(user.profile)
|
|
||||||
now = now.date()
|
|
||||||
start_day_of_month = now.replace(day=1)
|
|
||||||
|
|
||||||
top_games = (
|
|
||||||
BoardGame.objects.filter(
|
|
||||||
scrobble__user=user,
|
|
||||||
scrobble__timestamp__gte=start_day_of_month,
|
|
||||||
scrobble__played_to_completion=True,
|
|
||||||
)
|
|
||||||
.annotate(num_plays=Count("scrobble", distinct=True))
|
|
||||||
.order_by("-num_plays")[:10]
|
|
||||||
)
|
|
||||||
|
|
||||||
context["top_board_games"] = list(top_games)
|
|
||||||
context["month"] = now.strftime("%B %Y")
|
|
||||||
|
|
||||||
return context
|
|
||||||
|
|
||||||
|
|
||||||
class EmbeddableTopBoardGamesWeekWidget(TemplateView):
|
|
||||||
template_name = "scrobbles/embeddable_top_board_games_week.html"
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
|
||||||
from django.db.models import Count
|
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from datetime import timedelta
|
|
||||||
from boardgames.models import BoardGame
|
|
||||||
|
|
||||||
context = super().get_context_data(**kwargs)
|
|
||||||
user_id = self.kwargs.get("user_id")
|
|
||||||
|
|
||||||
try:
|
|
||||||
user = User.objects.get(id=user_id)
|
|
||||||
except User.DoesNotExist:
|
|
||||||
context["error"] = "User not found"
|
|
||||||
return context
|
|
||||||
|
|
||||||
profile = user.profile
|
|
||||||
if not profile.enable_public_widgets:
|
|
||||||
raise Http404("Public widgets are not enabled for this user")
|
|
||||||
|
|
||||||
context["user"] = user
|
|
||||||
context["custom_css"] = profile.widget_custom_css
|
|
||||||
|
|
||||||
now = timezone.now()
|
now = timezone.now()
|
||||||
if user.is_authenticated:
|
if user.is_authenticated:
|
||||||
now = now_user_timezone(user.profile)
|
now = now_user_timezone(user.profile)
|
||||||
now = now.date()
|
now = now.date()
|
||||||
start_day_of_week = now - timedelta(days=now.isoweekday() % 7)
|
|
||||||
|
|
||||||
top_games = (
|
if period == "month":
|
||||||
BoardGame.objects.filter(
|
context["period_label"] = now.strftime("%B %Y")
|
||||||
scrobble__user=user,
|
elif period == "year":
|
||||||
scrobble__timestamp__gte=start_day_of_week,
|
context["period_label"] = now.strftime("%Y")
|
||||||
scrobble__played_to_completion=True,
|
else:
|
||||||
)
|
start = now - timedelta(days=now.isoweekday() % 7)
|
||||||
.annotate(num_plays=Count("scrobble", distinct=True))
|
context["period_label"] = "This Week"
|
||||||
.order_by("-num_plays")[:10]
|
|
||||||
)
|
|
||||||
|
|
||||||
context["top_board_games"] = list(top_games)
|
|
||||||
|
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
class EmbeddableTopBoardGamesYearWidget(TemplateView):
|
class EmbeddableTopBoardGamesWidget(BaseEmbeddableWidget):
|
||||||
template_name = "scrobbles/embeddable_top_board_games_year.html"
|
media_type = "Board Games"
|
||||||
|
count_label = "plays"
|
||||||
|
no_data_message = "No board games played"
|
||||||
|
scrobble_filter = {"scrobble__played_to_completion": True}
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_items(self, user, start_date):
|
||||||
from django.db.models import Count
|
|
||||||
from django.utils import timezone
|
|
||||||
from boardgames.models import BoardGame
|
from boardgames.models import BoardGame
|
||||||
|
|
||||||
context = super().get_context_data(**kwargs)
|
return super().get_items(user, start_date, BoardGame)
|
||||||
user_id = self.kwargs.get("user_id")
|
|
||||||
|
|
||||||
try:
|
|
||||||
user = User.objects.get(id=user_id)
|
|
||||||
except User.DoesNotExist:
|
|
||||||
context["error"] = "User not found"
|
|
||||||
return context
|
|
||||||
|
|
||||||
profile = user.profile
|
class EmbeddableTopBooksWidget(BaseEmbeddableWidget):
|
||||||
if not profile.enable_public_widgets:
|
media_type = "Books"
|
||||||
raise Http404("Public widgets are not enabled for this user")
|
count_label = "reads"
|
||||||
|
no_data_message = "No books read"
|
||||||
|
scrobble_filter = {"scrobble__long_play_complete": True}
|
||||||
|
|
||||||
context["user"] = user
|
def get_items(self, user, start_date):
|
||||||
context["custom_css"] = profile.widget_custom_css
|
from books.models import Book
|
||||||
|
|
||||||
now = timezone.now()
|
return super().get_items(user, start_date, Book)
|
||||||
if user.is_authenticated:
|
|
||||||
now = now_user_timezone(user.profile)
|
|
||||||
now = now.date()
|
|
||||||
start_day_of_year = now.replace(month=1, day=1)
|
|
||||||
|
|
||||||
top_games = (
|
|
||||||
BoardGame.objects.filter(
|
|
||||||
scrobble__user=user,
|
|
||||||
scrobble__timestamp__gte=start_day_of_year,
|
|
||||||
scrobble__played_to_completion=True,
|
|
||||||
)
|
|
||||||
.annotate(num_plays=Count("scrobble", distinct=True))
|
|
||||||
.order_by("-num_plays")[:10]
|
|
||||||
)
|
|
||||||
|
|
||||||
context["top_board_games"] = list(top_games)
|
|
||||||
context["year"] = now.strftime("%Y")
|
|
||||||
|
|
||||||
return context
|
|
||||||
|
|||||||
@ -111,10 +111,15 @@
|
|||||||
<div class="widget-links">
|
<div class="widget-links">
|
||||||
<strong>Available Widgets:</strong>
|
<strong>Available Widgets:</strong>
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="{% url 'scrobbles:embeddable-top-artists' user_id %}">Top Artists</a></li>
|
<li><a href="{% url 'scrobbles:embeddable-top-artists' 'week' user_id %}">Top Artists (Week)</a></li>
|
||||||
<li><a href="{% url 'scrobbles:embeddable-top-board-games-week' user_id %}">Top Board Games (Week)</a></li>
|
<li><a href="{% url 'scrobbles:embeddable-top-artists' 'month' user_id %}">Top Artists (Month)</a></li>
|
||||||
<li><a href="{% url 'scrobbles:embeddable-top-board-games-month' user_id %}">Top Board Games (Month)</a></li>
|
<li><a href="{% url 'scrobbles:embeddable-top-artists' 'year' user_id %}">Top Artists (Year)</a></li>
|
||||||
<li><a href="{% url 'scrobbles:embeddable-top-board-games-year' user_id %}">Top Board Games (Year)</a></li>
|
<li><a href="{% url 'scrobbles:embeddable-top-board-games' 'week' user_id %}">Top Board Games (Week)</a></li>
|
||||||
|
<li><a href="{% url 'scrobbles:embeddable-top-board-games' 'month' user_id %}">Top Board Games (Month)</a></li>
|
||||||
|
<li><a href="{% url 'scrobbles:embeddable-top-board-games' 'year' user_id %}">Top Board Games (Year)</a></li>
|
||||||
|
<li><a href="{% url 'scrobbles:embeddable-top-books' 'week' user_id %}">Top Books (Week)</a></li>
|
||||||
|
<li><a href="{% url 'scrobbles:embeddable-top-books' 'month' user_id %}">Top Books (Month)</a></li>
|
||||||
|
<li><a href="{% url 'scrobbles:embeddable-top-books' 'year' user_id %}">Top Books (Year)</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|||||||
@ -1,90 +0,0 @@
|
|||||||
{% load static %}
|
|
||||||
<style>
|
|
||||||
.vrobbler-widget {
|
|
||||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
|
||||||
max-width: 400px;
|
|
||||||
border: 1px solid #e0e0e0;
|
|
||||||
border-radius: 8px;
|
|
||||||
padding: 16px;
|
|
||||||
background: #fff;
|
|
||||||
}
|
|
||||||
.vrobbler-widget h3 {
|
|
||||||
margin: 0 0 12px 0;
|
|
||||||
font-size: 16px;
|
|
||||||
color: #333;
|
|
||||||
}
|
|
||||||
.vrobbler-artist-list {
|
|
||||||
list-style: none;
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
.vrobbler-artist-item {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
padding: 8px 0;
|
|
||||||
border-bottom: 1px solid #f0f0f0;
|
|
||||||
}
|
|
||||||
.vrobbler-artist-item:last-child {
|
|
||||||
border-bottom: none;
|
|
||||||
}
|
|
||||||
.vrobbler-rank {
|
|
||||||
width: 24px;
|
|
||||||
font-weight: bold;
|
|
||||||
color: #666;
|
|
||||||
}
|
|
||||||
.vrobbler-artist-info {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
.vrobbler-artist-name {
|
|
||||||
font-weight: 500;
|
|
||||||
color: #333;
|
|
||||||
}
|
|
||||||
.vrobbler-scrobble-count {
|
|
||||||
font-size: 12px;
|
|
||||||
color: #888;
|
|
||||||
}
|
|
||||||
.vrobbler-artist-thumb {
|
|
||||||
width: 40px;
|
|
||||||
height: 40px;
|
|
||||||
border-radius: 4px;
|
|
||||||
object-fit: cover;
|
|
||||||
margin-right: 10px;
|
|
||||||
}
|
|
||||||
.vrobbler-error {
|
|
||||||
color: #d32f2f;
|
|
||||||
}
|
|
||||||
.vrobbler-no-data {
|
|
||||||
color: #666;
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
{% if custom_css %}
|
|
||||||
{{ custom_css }}
|
|
||||||
{% endif %}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<div class="vrobbler-widget">
|
|
||||||
<h3>Last 7 Days Top Artists{% if user %} - {{ user.username }}{% endif %}</h3>
|
|
||||||
|
|
||||||
{% if error %}
|
|
||||||
<p class="vrobbler-error">{{ error }}</p>
|
|
||||||
{% elif top_artists %}
|
|
||||||
<ul class="vrobbler-artist-list">
|
|
||||||
{% for artist in top_artists %}
|
|
||||||
<li class="vrobbler-artist-item">
|
|
||||||
<span class="vrobbler-rank">{{ forloop.counter }}</span>
|
|
||||||
{% if artist.thumbnail %}
|
|
||||||
<img class="vrobbler-artist-thumb" src="{{ artist.thumbnail_small.url }}" alt="{{ artist.name }}">
|
|
||||||
{% else %}
|
|
||||||
<img class="vrobbler-artist-thumb" src="{% static 'images/not-found.jpg' %}" alt="{{ artist.name }}">
|
|
||||||
{% endif %}
|
|
||||||
<div class="vrobbler-artist-info">
|
|
||||||
<div class="vrobbler-artist-name">{{ artist.name }}</div>
|
|
||||||
<div class="vrobbler-scrobble-count">{{ artist.num_scrobbles }} scrobbles</div>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
{% else %}
|
|
||||||
<p class="vrobbler-no-data">No scrobbles in the last 7 days</p>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
@ -1,90 +0,0 @@
|
|||||||
{% load static %}
|
|
||||||
<style>
|
|
||||||
.vrobbler-widget {
|
|
||||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
|
||||||
max-width: 400px;
|
|
||||||
border: 1px solid #e0e0e0;
|
|
||||||
border-radius: 8px;
|
|
||||||
padding: 16px;
|
|
||||||
background: #fff;
|
|
||||||
}
|
|
||||||
.vrobbler-widget h3 {
|
|
||||||
margin: 0 0 12px 0;
|
|
||||||
font-size: 16px;
|
|
||||||
color: #333;
|
|
||||||
}
|
|
||||||
.vrobbler-board-game-list {
|
|
||||||
list-style: none;
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
.vrobbler-board-game-item {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
padding: 8px 0;
|
|
||||||
border-bottom: 1px solid #f0f0f0;
|
|
||||||
}
|
|
||||||
.vrobbler-board-game-item:last-child {
|
|
||||||
border-bottom: none;
|
|
||||||
}
|
|
||||||
.vrobbler-rank {
|
|
||||||
width: 24px;
|
|
||||||
font-weight: bold;
|
|
||||||
color: #666;
|
|
||||||
}
|
|
||||||
.vrobbler-board-game-info {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
.vrobbler-board-game-title {
|
|
||||||
font-weight: 500;
|
|
||||||
color: #333;
|
|
||||||
}
|
|
||||||
.vrobbler-play-count {
|
|
||||||
font-size: 12px;
|
|
||||||
color: #888;
|
|
||||||
}
|
|
||||||
.vrobbler-board-game-thumb {
|
|
||||||
width: 40px;
|
|
||||||
height: 40px;
|
|
||||||
border-radius: 4px;
|
|
||||||
object-fit: cover;
|
|
||||||
margin-right: 10px;
|
|
||||||
}
|
|
||||||
.vrobbler-error {
|
|
||||||
color: #d32f2f;
|
|
||||||
}
|
|
||||||
.vrobbler-no-data {
|
|
||||||
color: #666;
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
{% if custom_css %}
|
|
||||||
{{ custom_css }}
|
|
||||||
{% endif %}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<div class="vrobbler-widget">
|
|
||||||
<h3>Top Board Games - This Week{% if user %} - {{ user.username }}{% endif %}</h3>
|
|
||||||
|
|
||||||
{% if error %}
|
|
||||||
<p class="vrobbler-error">{{ error }}</p>
|
|
||||||
{% elif top_board_games %}
|
|
||||||
<ul class="vrobbler-board-game-list">
|
|
||||||
{% for game in top_board_games %}
|
|
||||||
<li class="vrobbler-board-game-item">
|
|
||||||
<span class="vrobbler-rank">{{ forloop.counter }}</span>
|
|
||||||
{% if game.cover %}
|
|
||||||
<img class="vrobbler-board-game-thumb" src="{{ game.cover_small.url }}" alt="{{ game.title }}">
|
|
||||||
{% else %}
|
|
||||||
<img class="vrobbler-board-game-thumb" src="{% static 'images/not-found.jpg' %}" alt="{{ game.title }}">
|
|
||||||
{% endif %}
|
|
||||||
<div class="vrobbler-board-game-info">
|
|
||||||
<div class="vrobbler-board-game-title">{{ game.title }}</div>
|
|
||||||
<div class="vrobbler-play-count">{{ game.num_plays }} plays</div>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
{% else %}
|
|
||||||
<p class="vrobbler-no-data">No board games played this week</p>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
@ -1,90 +0,0 @@
|
|||||||
{% load static %}
|
|
||||||
<style>
|
|
||||||
.vrobbler-widget {
|
|
||||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
|
||||||
max-width: 400px;
|
|
||||||
border: 1px solid #e0e0e0;
|
|
||||||
border-radius: 8px;
|
|
||||||
padding: 16px;
|
|
||||||
background: #fff;
|
|
||||||
}
|
|
||||||
.vrobbler-widget h3 {
|
|
||||||
margin: 0 0 12px 0;
|
|
||||||
font-size: 16px;
|
|
||||||
color: #333;
|
|
||||||
}
|
|
||||||
.vrobbler-board-game-list {
|
|
||||||
list-style: none;
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
.vrobbler-board-game-item {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
padding: 8px 0;
|
|
||||||
border-bottom: 1px solid #f0f0f0;
|
|
||||||
}
|
|
||||||
.vrobbler-board-game-item:last-child {
|
|
||||||
border-bottom: none;
|
|
||||||
}
|
|
||||||
.vrobbler-rank {
|
|
||||||
width: 24px;
|
|
||||||
font-weight: bold;
|
|
||||||
color: #666;
|
|
||||||
}
|
|
||||||
.vrobbler-board-game-info {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
.vrobbler-board-game-title {
|
|
||||||
font-weight: 500;
|
|
||||||
color: #333;
|
|
||||||
}
|
|
||||||
.vrobbler-play-count {
|
|
||||||
font-size: 12px;
|
|
||||||
color: #888;
|
|
||||||
}
|
|
||||||
.vrobbler-board-game-thumb {
|
|
||||||
width: 40px;
|
|
||||||
height: 40px;
|
|
||||||
border-radius: 4px;
|
|
||||||
object-fit: cover;
|
|
||||||
margin-right: 10px;
|
|
||||||
}
|
|
||||||
.vrobbler-error {
|
|
||||||
color: #d32f2f;
|
|
||||||
}
|
|
||||||
.vrobbler-no-data {
|
|
||||||
color: #666;
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
{% if custom_css %}
|
|
||||||
{{ custom_css }}
|
|
||||||
{% endif %}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<div class="vrobbler-widget">
|
|
||||||
<h3>Top Board Games - {{ year }}{% if user %} - {{ user.username }}{% endif %}</h3>
|
|
||||||
|
|
||||||
{% if error %}
|
|
||||||
<p class="vrobbler-error">{{ error }}</p>
|
|
||||||
{% elif top_board_games %}
|
|
||||||
<ul class="vrobbler-board-game-list">
|
|
||||||
{% for game in top_board_games %}
|
|
||||||
<li class="vrobbler-board-game-item">
|
|
||||||
<span class="vrobbler-rank">{{ forloop.counter }}</span>
|
|
||||||
{% if game.cover %}
|
|
||||||
<img class="vrobbler-board-game-thumb" src="{{ game.cover_small.url }}" alt="{{ game.title }}">
|
|
||||||
{% else %}
|
|
||||||
<img class="vrobbler-board-game-thumb" src="{% static 'images/not-found.jpg' %}" alt="{{ game.title }}">
|
|
||||||
{% endif %}
|
|
||||||
<div class="vrobbler-board-game-info">
|
|
||||||
<div class="vrobbler-board-game-title">{{ game.title }}</div>
|
|
||||||
<div class="vrobbler-play-count">{{ game.num_plays }} plays</div>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
{% else %}
|
|
||||||
<p class="vrobbler-no-data">No board games played this year</p>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
@ -13,18 +13,18 @@
|
|||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
color: #333;
|
color: #333;
|
||||||
}
|
}
|
||||||
.vrobbler-board-game-list {
|
.vrobbler-media-list {
|
||||||
list-style: none;
|
list-style: none;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
.vrobbler-board-game-item {
|
.vrobbler-media-item {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 8px 0;
|
padding: 8px 0;
|
||||||
border-bottom: 1px solid #f0f0f0;
|
border-bottom: 1px solid #f0f0f0;
|
||||||
}
|
}
|
||||||
.vrobbler-board-game-item:last-child {
|
.vrobbler-media-item:last-child {
|
||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
}
|
}
|
||||||
.vrobbler-rank {
|
.vrobbler-rank {
|
||||||
@ -32,18 +32,18 @@
|
|||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
color: #666;
|
color: #666;
|
||||||
}
|
}
|
||||||
.vrobbler-board-game-info {
|
.vrobbler-media-info {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
.vrobbler-board-game-title {
|
.vrobbler-media-title {
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
color: #333;
|
color: #333;
|
||||||
}
|
}
|
||||||
.vrobbler-play-count {
|
.vrobbler-media-count {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
color: #888;
|
color: #888;
|
||||||
}
|
}
|
||||||
.vrobbler-board-game-thumb {
|
.vrobbler-media-thumb {
|
||||||
width: 40px;
|
width: 40px;
|
||||||
height: 40px;
|
height: 40px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
@ -63,28 +63,30 @@
|
|||||||
</style>
|
</style>
|
||||||
|
|
||||||
<div class="vrobbler-widget">
|
<div class="vrobbler-widget">
|
||||||
<h3>Top Board Games - {{ month }}{% if user %} - {{ user.username }}{% endif %}</h3>
|
<h3>Top {{ media_type }} - {{ period_label }}{% if user %} - {{ user.username }}{% endif %}</h3>
|
||||||
|
|
||||||
{% if error %}
|
{% if error %}
|
||||||
<p class="vrobbler-error">{{ error }}</p>
|
<p class="vrobbler-error">{{ error }}</p>
|
||||||
{% elif top_board_games %}
|
{% elif items %}
|
||||||
<ul class="vrobbler-board-game-list">
|
<ul class="vrobbler-media-list">
|
||||||
{% for game in top_board_games %}
|
{% for item in items %}
|
||||||
<li class="vrobbler-board-game-item">
|
<li class="vrobbler-media-item">
|
||||||
<span class="vrobbler-rank">{{ forloop.counter }}</span>
|
<span class="vrobbler-rank">{{ forloop.counter }}</span>
|
||||||
{% if game.cover %}
|
{% if item.cover %}
|
||||||
<img class="vrobbler-board-game-thumb" src="{{ game.cover_small.url }}" alt="{{ game.title }}">
|
<img class="vrobbler-media-thumb" src="{{ item.cover_small.url }}" alt="{{ item.name }}">
|
||||||
|
{% elif item.thumbnail %}
|
||||||
|
<img class="vrobbler-media-thumb" src="{{ item.thumbnail_small.url }}" alt="{{ item.name }}">
|
||||||
{% else %}
|
{% else %}
|
||||||
<img class="vrobbler-board-game-thumb" src="{% static 'images/not-found.jpg' %}" alt="{{ game.title }}">
|
<img class="vrobbler-media-thumb" src="{% static 'images/not-found.jpg' %}" alt="{{ item.name }}">
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<div class="vrobbler-board-game-info">
|
<div class="vrobbler-media-info">
|
||||||
<div class="vrobbler-board-game-title">{{ game.title }}</div>
|
<div class="vrobbler-media-title">{{ item.name }}</div>
|
||||||
<div class="vrobbler-play-count">{{ game.num_plays }} plays</div>
|
<div class="vrobbler-media-count">{{ item.count }} {{ count_label }}</div>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
{% else %}
|
{% else %}
|
||||||
<p class="vrobbler-no-data">No board games played this month</p>
|
<p class="vrobbler-no-data">{{ no_data_message }}</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
Reference in New Issue
Block a user