[charts] Clean up how they're displayed
This commit is contained in:
@ -110,14 +110,13 @@ class ChartRecord(TimeStampedModel):
|
||||
|
||||
@property
|
||||
def period_str(self) -> str:
|
||||
period = str(self.year)
|
||||
if self.month:
|
||||
period = f"{calendar.month_name[self.month]} {period}"
|
||||
if self.week:
|
||||
period = f"Week {self.week}, {period}"
|
||||
if self.day:
|
||||
period = f"{calendar.month_name[self.month]} {self.day}, {period}"
|
||||
return period
|
||||
return f"{calendar.month_name[self.month]} {self.day}, {self.year}"
|
||||
if self.week:
|
||||
return f"Week {self.week}, {self.year}"
|
||||
if self.month:
|
||||
return f"{calendar.month_name[self.month]} {self.year}"
|
||||
return str(self.year)
|
||||
|
||||
@property
|
||||
def period_type(self) -> str:
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
from charts.views import BirdsChartView, ChartRecordView, SpotifyTracksView
|
||||
from charts.views import BirdsChartView, ChartDetailView, ChartRecordView, SpotifyTracksView
|
||||
from django.urls import path
|
||||
|
||||
app_name = "charts"
|
||||
@ -7,4 +7,5 @@ urlpatterns = [
|
||||
path("charts/", ChartRecordView.as_view(), name="charts-home"),
|
||||
path("charts/spotify/", SpotifyTracksView.as_view(), name="spotify-tracks"),
|
||||
path("charts/birds/", BirdsChartView.as_view(), name="birds-chart"),
|
||||
path("charts/<slug:media_type>/", ChartDetailView.as_view(), name="chart-detail"),
|
||||
]
|
||||
|
||||
@ -3,6 +3,7 @@ from datetime import timedelta
|
||||
|
||||
from charts.models import ChartRecord
|
||||
from django.db.models import Count, Q
|
||||
from django.http import Http404
|
||||
from django.utils import timezone
|
||||
from django.views.generic import TemplateView
|
||||
from profiles.utils import now_user_timezone
|
||||
@ -617,6 +618,103 @@ class ChartRecordView(TemplateView):
|
||||
}
|
||||
|
||||
|
||||
MEDIA_TYPE_LABELS = {
|
||||
"artist": ("🎤", "Top Artists"),
|
||||
"album": ("💿", "Top Albums"),
|
||||
"track": ("🎵", "Top Tracks"),
|
||||
"tv_series": ("📺", "Top TV Series"),
|
||||
"video": ("🎬", "Top Videos"),
|
||||
"podcast": ("🎙️", "Top Podcasts"),
|
||||
"podcast_episode": ("🎙️", "Top Podcast Episodes"),
|
||||
"board_game": ("🎲", "Top Board Games"),
|
||||
"book": ("📚", "Top Books"),
|
||||
"food": ("🍽️", "Top Foods"),
|
||||
"trail": ("🥾", "Top Trails"),
|
||||
"geo_location": ("📍", "Top Locations"),
|
||||
}
|
||||
|
||||
|
||||
class ChartDetailView(TemplateView):
|
||||
template_name = "charts/chart_detail.html"
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
user = self.request.user
|
||||
media_type = kwargs.get("media_type")
|
||||
|
||||
if media_type not in MEDIA_TYPE_FILTERS:
|
||||
raise Http404
|
||||
|
||||
date_param = self.request.GET.get("date")
|
||||
now = timezone.now()
|
||||
if user.is_authenticated:
|
||||
now = now_user_timezone(user.profile)
|
||||
today = now.date()
|
||||
|
||||
year = today.year
|
||||
month = None
|
||||
week = None
|
||||
day = None
|
||||
|
||||
if date_param:
|
||||
parts = date_param.split("-")
|
||||
year = int(parts[0])
|
||||
if len(parts) >= 2 and parts[1].startswith("W"):
|
||||
week = int(parts[1].lstrip("W"))
|
||||
month = None
|
||||
elif len(parts) >= 2:
|
||||
try:
|
||||
month = int(parts[1])
|
||||
except ValueError:
|
||||
pass
|
||||
if len(parts) >= 3:
|
||||
if parts[2].startswith("W"):
|
||||
week = int(parts[2].lstrip("W"))
|
||||
else:
|
||||
day = int(parts[2])
|
||||
|
||||
params = {"user": user}
|
||||
if year:
|
||||
params["year"] = year
|
||||
if month:
|
||||
params["month"] = month
|
||||
if week:
|
||||
params["week"] = week
|
||||
if day:
|
||||
params["day"] = day
|
||||
|
||||
media_filter = MEDIA_TYPE_FILTERS.get(media_type, Q())
|
||||
qs = ChartRecord.objects.filter(media_filter, **params)
|
||||
|
||||
if day is not None:
|
||||
qs = qs.filter(day__isnull=False)
|
||||
elif week is not None:
|
||||
qs = qs.filter(week__isnull=False, day__isnull=True)
|
||||
elif month is not None:
|
||||
qs = qs.filter(month__isnull=False, week__isnull=True, day__isnull=True)
|
||||
else:
|
||||
qs = qs.filter(month__isnull=True, week__isnull=True, day__isnull=True)
|
||||
|
||||
context["page_charts"] = qs.order_by("rank")
|
||||
context["media_type"] = media_type
|
||||
emoji, label = MEDIA_TYPE_LABELS.get(media_type, ("", media_type))
|
||||
context["media_label"] = label
|
||||
context["media_emoji"] = emoji
|
||||
context["year"] = year
|
||||
context["month"] = month
|
||||
context["week"] = week
|
||||
context["day"] = day
|
||||
|
||||
context["chart_years"] = list(
|
||||
ChartRecord.objects.filter(Q(user=user) & media_filter)
|
||||
.values_list("year", flat=True)
|
||||
.distinct()
|
||||
.order_by("-year")
|
||||
)
|
||||
|
||||
return context
|
||||
|
||||
|
||||
class SpotifyTracksView(TemplateView):
|
||||
template_name = "charts/spotify_tracks.html"
|
||||
|
||||
|
||||
@ -14,13 +14,6 @@ class TrackListView(ScrobbleableListView):
|
||||
class TrackDetailView(ScrobbleableDetailView):
|
||||
model = Track
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context_data = super().get_context_data(**kwargs)
|
||||
context_data["charts"] = ChartRecord.objects.filter(
|
||||
track=self.object, rank__in=[1, 2, 3]
|
||||
)
|
||||
return context_data
|
||||
|
||||
|
||||
class ArtistListView(generic.ListView):
|
||||
model = Artist
|
||||
@ -57,9 +50,16 @@ class ArtistDetailView(generic.DetailView):
|
||||
]
|
||||
|
||||
context_data["tracks_ranked"] = tracks_ranked
|
||||
context_data["charts"] = ChartRecord.objects.filter(
|
||||
|
||||
charts_qs = ChartRecord.objects.filter(
|
||||
artist=self.object, rank__in=[1, 2, 3]
|
||||
)
|
||||
).exclude(day__isnull=False)
|
||||
from collections import OrderedDict
|
||||
|
||||
grouped = OrderedDict()
|
||||
for chart in charts_qs:
|
||||
grouped.setdefault(chart.period_type, []).append(chart)
|
||||
context_data["charts"] = grouped
|
||||
|
||||
from scrobbles.models import Scrobble
|
||||
|
||||
|
||||
@ -135,9 +135,9 @@ class ChartContextMixin:
|
||||
return context
|
||||
|
||||
media_type_map = {
|
||||
"music.Artist": "artist",
|
||||
"music.Album": "album",
|
||||
"music.Track": "track",
|
||||
"music.artist": "artist",
|
||||
"music.album": "album",
|
||||
"music.track": "track",
|
||||
"videos.video": "video",
|
||||
"videos.series": "tv_series",
|
||||
"podcasts.podcast": "podcast",
|
||||
@ -151,10 +151,17 @@ class ChartContextMixin:
|
||||
media_type = media_type_map.get(model_label)
|
||||
|
||||
if media_type:
|
||||
context["charts"] = ChartRecord.objects.filter(
|
||||
charts_qs = ChartRecord.objects.filter(
|
||||
**{media_type: obj}, rank__in=[1, 2, 3]
|
||||
).exclude(day__isnull=False)
|
||||
|
||||
from collections import OrderedDict
|
||||
|
||||
grouped = OrderedDict()
|
||||
for chart in charts_qs:
|
||||
grouped.setdefault(chart.period_type, []).append(chart)
|
||||
context["charts"] = grouped
|
||||
|
||||
return context
|
||||
|
||||
|
||||
|
||||
45
vrobbler/templates/charts/chart_detail.html
Normal file
45
vrobbler/templates/charts/chart_detail.html
Normal file
@ -0,0 +1,45 @@
|
||||
{% extends "base_list.html" %}
|
||||
|
||||
{% block title %}{{ media_label }}{% if year %} - {{ year }}{% endif %}{% endblock %}
|
||||
|
||||
{% block head_extra %}
|
||||
<style>
|
||||
.container { margin-bottom: 100px; }
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block lists %}
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<h2>{{ media_emoji }} {{ media_label }}</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mb-3">
|
||||
<div class="col-12">
|
||||
<div class="btn-group" role="group">
|
||||
<a href="{% url 'charts:chart-detail' media_type %}" class="btn btn-outline-secondary{% if not year or year == today.year and not month and not week and not day %} active{% endif %}">All</a>
|
||||
{% for cy in chart_years %}
|
||||
<a href="{% url 'charts:chart-detail' media_type %}?date={{ cy }}" class="btn btn-outline-secondary{% if year == cy and not month and not week %} active{% endif %}">{{ cy }}</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<a href="{% url 'charts:charts-home' %}" class="btn btn-outline-primary btn-sm ms-2">« All charts</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-8 col-lg-6">
|
||||
<ul class="list-group">
|
||||
{% for chart in page_charts %}
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||
<span class="me-2"><strong>#{{ chart.rank }}</strong></span>
|
||||
<a href="{{ chart.media_obj.get_absolute_url }}">{{ chart.media_obj }}</a>
|
||||
<span class="badge bg-primary rounded-pill">{{ chart.count }}</span>
|
||||
</li>
|
||||
{% empty %}
|
||||
<li class="list-group-item">No chart data for this period.</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@ -62,6 +62,9 @@
|
||||
<span class="badge bg-primary rounded-pill">{{chart.count}}</span>
|
||||
</li>
|
||||
{% endfor %}
|
||||
<li class="list-group-item">
|
||||
<a href="{% url 'charts:chart-detail' 'artist' %}">View all »</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
@ -77,6 +80,9 @@
|
||||
<span class="badge bg-primary rounded-pill">{{chart.count}}</span>
|
||||
</li>
|
||||
{% endfor %}
|
||||
<li class="list-group-item">
|
||||
<a href="{% url 'charts:chart-detail' 'album' %}">View all »</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
@ -92,6 +98,9 @@
|
||||
<span class="badge bg-primary rounded-pill">{{chart.count}}</span>
|
||||
</li>
|
||||
{% endfor %}
|
||||
<li class="list-group-item">
|
||||
<a href="{% url 'charts:chart-detail' 'track' %}">View all »</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
@ -107,6 +116,9 @@
|
||||
<span class="badge bg-primary rounded-pill">{{chart.count}}</span>
|
||||
</li>
|
||||
{% endfor %}
|
||||
<li class="list-group-item">
|
||||
<a href="{% url 'charts:chart-detail' 'tv_series' %}">View all »</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
@ -122,6 +134,9 @@
|
||||
<span class="badge bg-primary rounded-pill">{{chart.count}}</span>
|
||||
</li>
|
||||
{% endfor %}
|
||||
<li class="list-group-item">
|
||||
<a href="{% url 'charts:chart-detail' 'video' %}">View all »</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
@ -137,6 +152,9 @@
|
||||
<span class="badge bg-primary rounded-pill">{{chart.count}}</span>
|
||||
</li>
|
||||
{% endfor %}
|
||||
<li class="list-group-item">
|
||||
<a href="{% url 'charts:chart-detail' 'podcast' %}">View all »</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
@ -152,6 +170,9 @@
|
||||
<span class="badge bg-primary rounded-pill">{{chart.count}}</span>
|
||||
</li>
|
||||
{% endfor %}
|
||||
<li class="list-group-item">
|
||||
<a href="{% url 'charts:chart-detail' 'board_game' %}">View all »</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
@ -167,6 +188,9 @@
|
||||
<span class="badge bg-primary rounded-pill">{{chart.count}}</span>
|
||||
</li>
|
||||
{% endfor %}
|
||||
<li class="list-group-item">
|
||||
<a href="{% url 'charts:chart-detail' 'book' %}">View all »</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
@ -182,6 +206,9 @@
|
||||
<span class="badge bg-primary rounded-pill">{{chart.count}}</span>
|
||||
</li>
|
||||
{% endfor %}
|
||||
<li class="list-group-item">
|
||||
<a href="{% url 'charts:chart-detail' 'food' %}">View all »</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
@ -197,6 +224,9 @@
|
||||
<span class="badge bg-primary rounded-pill">{{chart.count}}</span>
|
||||
</li>
|
||||
{% endfor %}
|
||||
<li class="list-group-item">
|
||||
<a href="{% url 'charts:chart-detail' 'trail' %}">View all »</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
@ -212,6 +242,9 @@
|
||||
<span class="badge bg-primary rounded-pill">{{chart.count}}</span>
|
||||
</li>
|
||||
{% endfor %}
|
||||
<li class="list-group-item">
|
||||
<a href="{% url 'charts:chart-detail' 'geo_location' %}">View all »</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
@ -1,7 +1,19 @@
|
||||
{% if charts %}
|
||||
<div class="row">
|
||||
<div class="col-md">
|
||||
<p>{% for chart in charts %}<em><a href="{{chart.chart_url}}">{{chart.rank_emoji}} {{chart.period_str}}</a></em>{% if forloop.last %}{% else %} | {% endif %}{% endfor %}</p>
|
||||
{% for period_type, chart_list in charts.items %}
|
||||
<div class="mb-1">
|
||||
<small class="text-muted">
|
||||
{% if period_type == "year" %}📅 Yearly
|
||||
{% elif period_type == "month" %}📆 Monthly
|
||||
{% elif period_type == "week" %}🗓️ Weekly
|
||||
{% endif %}
|
||||
</small>
|
||||
{% for chart in chart_list %}
|
||||
<a href="{{chart.chart_url}}">{{chart.rank_emoji}} {{chart.period_str}}</a>{% if not forloop.last %} · {% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
Reference in New Issue
Block a user