[charts] Clean up some chart views
All checks were successful
build & deploy / test (push) Successful in 1m58s
build & deploy / build-and-deploy (push) Successful in 29s

This commit is contained in:
2026-05-27 22:08:58 -04:00
parent 6d45571e75
commit d7a23c3832
6 changed files with 130 additions and 11 deletions

View File

@ -83,6 +83,12 @@
{% endfor %}
<a href="#" class="btn btn-xs btn-outline-secondary" style="padding:1px 3px;font-size:9px;" onclick="var w=prompt('Week number (1-52):');if(w)this.href='/charts/?date={{ year }}-W'+w.padStart(2,'0');return false;">+</a>
</div>
<div class="row mb-3">
<div class="col-12">
<a href="{% url 'charts:spotify-tracks' %}" class="btn btn-sm btn-outline-secondary">🎵 Spotify Tracks</a>
<a href="{% url 'charts:bandcamp-tracks' %}" class="btn btn-sm btn-outline-secondary">🎵 Bandcamp Tracks</a>
</div>
</div>
{% if chart_type == "maloja" %}
{% include "scrobbles/_top_charts.html" %}
{% else %}

View File

@ -1,4 +1,10 @@
from charts.views import BirdsChartView, ChartDetailView, ChartRecordView, SpotifyTracksView
from charts.views import (
BandcampTracksView,
BirdsChartView,
ChartDetailView,
ChartRecordView,
SpotifyTracksView,
)
from django.urls import path
app_name = "charts"
@ -6,6 +12,7 @@ app_name = "charts"
urlpatterns = [
path("charts/", ChartRecordView.as_view(), name="charts-home"),
path("charts/spotify/", SpotifyTracksView.as_view(), name="spotify-tracks"),
path("charts/bandcamp/", BandcampTracksView.as_view(), name="bandcamp-tracks"),
path("charts/birds/", BirdsChartView.as_view(), name="birds-chart"),
path("charts/<slug:media_type>/", ChartDetailView.as_view(), name="chart-detail"),
]

View File

@ -221,6 +221,7 @@ class ChartRecordView(TemplateView):
context["period"] = "current"
context["year"] = current_year
context["month"] = current_month
context["month_name"] = calendar.month_name[current_month]
context["week"] = current_week
context["day"] = current_day
@ -301,6 +302,7 @@ class ChartRecordView(TemplateView):
context["period"] = "historical"
context["year"] = year
context["month"] = month
context["month_name"] = calendar.month_name[month] if month else None
context["week"] = week
context["day"] = day
@ -604,9 +606,9 @@ class ChartRecordView(TemplateView):
if bird_id:
bird_counts[bird_id] = bird_counts.get(bird_id, 0) + quantity
sorted_birds = sorted(
bird_counts.items(), key=lambda x: x[1], reverse=True
)[:limit]
sorted_birds = sorted(bird_counts.items(), key=lambda x: x[1], reverse=True)[
:limit
]
bird_ids = [bid for bid, _ in sorted_birds]
birds = Bird.objects.filter(id__in=bird_ids)
@ -727,12 +729,22 @@ class SpotifyTracksView(TemplateView):
template_name = "charts/spotify_tracks.html"
def get_spotify_tracks(self, user, limit=50):
non_spotify_mopidy_tracks = (
Scrobble.objects.filter(
user=user,
source="Mopidy",
track__isnull=False,
)
.exclude(log__mopidy_source="spotify")
.values("track")
)
track_ids = (
Scrobble.objects.filter(
user=user,
track__isnull=False,
)
.filter(Q(source="Last.fm") | Q(log__mopidy_source="spotify"))
.exclude(track__in=non_spotify_mopidy_tracks)
.values("track")
.annotate(count=Count("id"))
.order_by("-count")[:limit]
@ -760,6 +772,53 @@ class SpotifyTracksView(TemplateView):
return context
class BandcampTracksView(TemplateView):
template_name = "charts/bandcamp_tracks.html"
def get_bandcamp_tracks(self, user, limit=50):
non_bandcamp_mopidy_tracks = (
Scrobble.objects.filter(
user=user,
source="Mopidy",
track__isnull=False,
)
.exclude(log__mopidy_source="bandcamp")
.values("track")
)
track_ids = (
Scrobble.objects.filter(
user=user,
track__isnull=False,
log__mopidy_source="bandcamp",
)
.exclude(track__in=non_bandcamp_mopidy_tracks)
.values("track")
.annotate(count=Count("id"))
.order_by("-count")[:limit]
)
from music.models import Track
track_id_list = [item["track"] for item in track_ids]
tracks = Track.objects.filter(id__in=track_id_list)
track_map = {t.id: t for t in tracks}
return [
{
"track": track_map[tid],
"count": next(
(item["count"] for item in track_ids if item["track"] == tid), 0
),
}
for tid in track_id_list
if tid in track_map
]
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
user = self.request.user
context["bandcamp_tracks"] = self.get_bandcamp_tracks(user)
return context
class BirdsChartView(TemplateView):
template_name = "charts/birds_chart.html"
@ -792,9 +851,9 @@ class BirdsChartView(TemplateView):
if bird_id:
bird_counts[bird_id] = bird_counts.get(bird_id, 0) + quantity
sorted_birds = sorted(
bird_counts.items(), key=lambda x: x[1], reverse=True
)[:limit]
sorted_birds = sorted(bird_counts.items(), key=lambda x: x[1], reverse=True)[
:limit
]
bird_ids = [bid for bid, _ in sorted_birds]
birds = Bird.objects.filter(id__in=bird_ids)

View File

@ -13,7 +13,6 @@
</div>
<div class="btn-group me-2">
<a href="{% url 'charts:charts-home' %}" class="btn btn-sm btn-outline-secondary">Charts</a>
<a href="{% url 'charts:spotify-tracks' %}" class="btn btn-sm btn-outline-secondary">Spotify Tracks</a>
</div>
{% endif %}
<div class="btn-group me-2">

View File

@ -0,0 +1,34 @@
{% extends "base_list.html" %}
{% block title %}Bandcamp Tracks{% endblock %}
{% block head_extra %}
<style>
.container { margin-bottom: 100px; }
</style>
{% endblock %}
{% block lists %}
<div class="row">
<div class="col-12">
<h2>🎵 Top Bandcamp Tracks</h2>
<p class="text-muted">Shows tracks scrobbled from Bandcamp via Mopidy</p>
</div>
</div>
<div class="row">
<div class="col-md-6 col-lg-4 chart-section">
<ul class="list-group">
{% for item in bandcamp_tracks %}
<li class="list-group-item d-flex justify-content-between align-items-center">
<span class="me-2"><strong>#{{ forloop.counter }}</strong></span>
<a href="{{ item.track.get_absolute_url }}">{{ item.track.title }}</a>
<span class="badge bg-success rounded-pill">{{ item.count }}</span>
</li>
{% empty %}
<li class="list-group-item">No Bandcamp tracks found.</li>
{% endfor %}
</ul>
</div>
</div>
{% endblock %}

View File

@ -17,6 +17,13 @@
{% block lists %}
<div class="row mb-3">
<div class="col-12">
<a href="{% url 'charts:spotify-tracks' %}" class="btn btn-sm btn-outline-secondary">🎵 Spotify Tracks</a>
<a href="{% url 'charts:bandcamp-tracks' %}" class="btn btn-sm btn-outline-secondary">🎵 Bandcamp Tracks</a>
</div>
</div>
{% if chart_type == "maloja" %}
{% include "scrobbles/_top_charts.html" %}
{% else %}
@ -24,9 +31,16 @@
<div class="row">
<div class="col-12">
<div class="btn-group mb-3" role="group">
{% if year %}<a href="?date={{year}}{% if month %}-{{month}}{% endif %}" class="btn btn-outline-secondary{% if not week and not day %} active{% endif %}">{{year}}{% if month %} {{month|date:"F"}}{% endif %}</a>{% endif %}
{% if week %}<span class="btn btn-outline-secondary active">Week {{week}}</span>{% endif %}
{% if day %}<span class="btn btn-outline-secondary active">{{day}}</span>{% endif %}
<a href="?date={{ year }}" class="btn btn-outline-secondary{% if period_type == 'year' %} active{% endif %}">{{ year }}</a>
{% if month %}
<a href="?date={{ year }}-{{ month|stringformat:'02d' }}" class="btn btn-outline-secondary{% if period_type == 'month' %} active{% endif %}">{{ month_name }}</a>
{% endif %}
{% if week %}
<a href="?date={{ year }}-W{{ week|stringformat:'02d' }}" class="btn btn-outline-secondary{% if period_type == 'week' %} active{% endif %}">Week {{ week }}</a>
{% endif %}
{% if day %}
<a href="?date={{ year }}-{{ month|stringformat:'02d' }}-{{ day|stringformat:'02d' }}" class="btn btn-outline-secondary{% if period_type == 'day' %} active{% endif %}">{{ day }}</a>
{% endif %}
</div>
<span class="ms-2">
{% if period_type == "day" %}