Browse Source

Fix chart display

Colin Powell 2 năm trước cách đây
mục cha
commit
a99dca246b

+ 19 - 2
vrobbler/apps/music/views.py

@@ -1,6 +1,8 @@
-from django.db.models import Count
+from datetime import timedelta
+from django.utils import timezone
 from django.views import generic
-from music.models import Track, Artist, Album
+from music.models import Album, Artist, Track
+from scrobbles.models import ChartRecord
 from scrobbles.stats import get_scrobble_count_qs
 
 
@@ -17,6 +19,14 @@ class TrackDetailView(generic.DetailView):
     model = Track
     slug_field = 'uuid'
 
+    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
@@ -29,6 +39,13 @@ class ArtistDetailView(generic.DetailView):
     model = Artist
     slug_field = 'uuid'
 
+    def get_context_data(self, **kwargs):
+        context_data = super().get_context_data(**kwargs)
+        context_data['charts'] = ChartRecord.objects.filter(
+            artist=self.object, rank__in=[1, 2, 3]
+        )
+        return context_data
+
 
 class AlbumListView(generic.ListView):
     model = Album

+ 16 - 1
vrobbler/apps/scrobbles/models.py

@@ -312,7 +312,22 @@ class ChartRecord(TimeStampedModel):
         return period
 
     def __str__(self):
-        return f"#{self.rank} in {self.period} - {self.media_obj}"
+        title = f"#{self.rank} in {self.period}"
+        if self.day or self.week:
+            title = f"#{self.rank} on {self.period}"
+        return title
+
+    def link(self):
+        get_params = f"?date={self.year}"
+        if self.week:
+            get_params = get_params = get_params + f"-W{self.week}"
+        if self.month:
+            get_params = get_params = get_params + f"-{self.month}"
+        if self.day:
+            get_params = get_params = get_params + f"-{self.day}"
+        if self.artist:
+            get_params = get_params + "&media=Artist"
+        return reverse('scrobbles:charts-home') + get_params
 
     @classmethod
     def build(cls, user, **kwargs):

+ 41 - 59
vrobbler/apps/scrobbles/views.py

@@ -2,7 +2,6 @@ import calendar
 import json
 import logging
 from datetime import datetime, timedelta
-from django.db.models.query import QuerySet
 
 import pytz
 from django.conf import settings
@@ -10,6 +9,7 @@ from django.contrib import messages
 from django.contrib.auth.mixins import LoginRequiredMixin
 from django.db.models import Q
 from django.db.models.fields import timezone
+from django.db.models.query import QuerySet
 from django.http import FileResponse, HttpResponseRedirect, JsonResponse
 from django.urls import reverse, reverse_lazy
 from django.utils import timezone
@@ -17,12 +17,7 @@ from django.views.decorators.csrf import csrf_exempt
 from django.views.generic import DetailView, FormView, TemplateView
 from django.views.generic.edit import CreateView
 from django.views.generic.list import ListView
-from music.aggregators import (
-    scrobble_counts,
-    top_artists,
-    top_tracks,
-    week_of_scrobbles,
-)
+from music.aggregators import scrobble_counts, week_of_scrobbles
 from rest_framework import status
 from rest_framework.decorators import (
     api_view,
@@ -62,6 +57,8 @@ from scrobbles.tasks import (
 )
 from scrobbles.thesportsdb import lookup_event_from_thesportsdb
 
+from vrobbler.apps.music.aggregators import live_charts
+
 logger = logging.getLogger(__name__)
 
 
@@ -406,19 +403,17 @@ class ChartRecordView(TemplateView):
     template_name = 'scrobbles/chart_index.html'
 
     @staticmethod
-    def get_media_filter(media_type: str = "Track"):
-        media_filter = Q()
-        if media_type == 'Track':
-            media_filter = Q(track__isnull=False)
-        if media_type == 'Artist':
-            media_filter = Q(artist__isnull=False)
-        if media_type == 'Series':
-            media_filter = Q(series__isnull=False)
-        if media_type == 'Video':
-            media_filter = Q(video__isnull=False)
-        return media_filter
-
-    def get_chart_records(self, media_type: str = "Track", **kwargs):
+    def get_media_filter(media_type: str = "") -> Q:
+        filters = {
+            "Track": Q(track__isnull=False),
+            "Artist": Q(artist__isnull=False),
+            "Series": Q(series__isnull=False),
+            "Video": Q(video__isnull=False),
+            "": Q(),
+        }
+        return filters[media_type]
+
+    def get_chart_records(self, media_type: str = "", **kwargs):
         media_filter = self.get_media_filter(media_type)
         charts = ChartRecord.objects.filter(
             media_filter, user=self.request.user, **kwargs
@@ -434,37 +429,24 @@ class ChartRecordView(TemplateView):
         return charts
 
     def get_chart(
-        self, period: str = "all_time", limit=15, media: str = "Track"
+        self, period: str = "all_time", limit=15, media: str = ""
     ) -> QuerySet:
-        chart = QuerySet()
         now = timezone.now()
-        if period == "all_time":
-            chart = self.get_chart_records(media_type=media)
+        params = {}
+        params['media_type'] = media
         if period == "today":
-            chart = self.get_chart_records(
-                media_type=media,
-                day=now.day,
-                month=now.month,
-                year=now.year,
-            )
+            params['day'] = now.day
+            params['month'] = now.month
+            params['year'] = now.year
         if period == "week":
-            chart = self.get_chart_records(
-                media_type=media,
-                year=now.year,
-                week=now.isocalendar()[1],
-            )
+            params['week'] = now.ioscalendar()[1]
+            params['year'] = now.year
         if period == "month":
-            chart = self.get_chart_records(
-                media_type=media,
-                year=now.year,
-                month=now.month,
-            )
+            params['month'] = now.month
+            params['year'] = now.year
         if period == "year":
-            chart = self.get_chart_records(
-                media_type=media,
-                year=now.year,
-            )
-        return chart[:limit]
+            params['year'] = now.year
+        return self.get_chart_records(**params)[:limit]
 
     def get_context_data(self, **kwargs):
         context_data = super().get_context_data(**kwargs)
@@ -475,21 +457,24 @@ class ChartRecordView(TemplateView):
         context_data["artist_charts"] = {}
 
         if not date:
+            artist_params = {'user': user, 'media_type': 'Artist'}
             context_data['artist_charts'] = {
-                "today": top_artists(user, filter="today")[:30],
-                "week": top_artists(user, filter="week")[:30],
-                "month": top_artists(user, filter="month")[:30],
-                "all": top_artists(user),
+                "today": live_charts(**artist_params, chart_period="today"),
+                "week": live_charts(**artist_params, chart_period="week"),
+                "month": live_charts(**artist_params, chart_period="month"),
+                "all": live_charts(**artist_params),
             }
 
+            track_params = {'user': user, 'media_type': 'Track'}
             context_data['track_charts'] = {
-                "today": top_tracks(user, filter="today")[:30],
-                "week": top_tracks(user, filter="week")[:30],
-                "month": top_tracks(user, filter="month")[:30],
-                "all": top_tracks(user),
+                "today": live_charts(**track_params, chart_period="today"),
+                "week": live_charts(**track_params, chart_period="week"),
+                "month": live_charts(**track_params, chart_period="month"),
+                "all": live_charts(**track_params),
             }
             return context_data
 
+        # Date provided, lookup past charts, returning nothing if it's now or in the future.
         now = timezone.now()
         year = now.year
         params = {'year': year}
@@ -531,7 +516,7 @@ class ChartRecordView(TemplateView):
             media_filter, user=self.request.user, **params
         ).order_by("rank")
 
-        if charts.count() == 0:
+        if charts.count() == 0 and not in_progress:
             ChartRecord.build(
                 user=self.request.user, model_str=media_type, **params
             )
@@ -539,11 +524,8 @@ class ChartRecordView(TemplateView):
                 media_filter, user=self.request.user, **params
             ).order_by("rank")
 
-        if in_progress:
-            # TODO recalculate
-            ...
-
+        context_data['media_type'] = media_type
         context_data['charts'] = charts
-        context_data['name'] = name
+        context_data['name'] = " ".join(["Top", media_type, "for", name])
         context_data['in_progress'] = in_progress
         return context_data

+ 3 - 0
vrobbler/templates/music/artist_detail.html

@@ -14,6 +14,9 @@
 </div>
 <div class="row">
     <p>{{artist.scrobbles.count}} scrobbles</p>
+    {% if charts %}
+    <p>{% for chart in charts %}<em><a href="{{chart.link}}">{{chart}}</a></em>{% if forloop.last %}{% else %} | {% endif %}{% endfor %}</p>
+    {% endif %}
     <div class="col-md">
         <h3>Top tracks</h3>
         <div class="table-responsive">

+ 36 - 8
vrobbler/templates/music/track_detail.html

@@ -1,13 +1,41 @@
-{% extends "base_detail.html" %}
+{% extends "base_list.html" %}
 
 {% block title %}{{object.title}}{% endblock %}
 
-{% block details %}
-<h2>Last scrobbles</h2>
-{% for scrobble in object.scrobble_set.all  %}
-<ul>
-    <li>{{scrobble.timestamp|date:"d M Y h:m"}} - <img src="{{object.album.cover_image.url}}" width=25 height=25 /> - {{object}}</li>
-</ul>
-{% endfor %}
+{% block lists %}
+<div class="row">
+    {% if track.album.cover_image %}
+    <p style="width:150px; float:left;"><img src="{{track.album.cover_image.url}}" width=150 height=150 /></p>
+    {% endif %}
+</div>
+<div class="row">
+    <p>{{object.scrobble_set.count}} scrobbles</p>
+    {% if charts %}
+    <p>{% for chart in charts %}<em><a href="{{chart.link}}">{{chart}}</a></em>{% if forloop.last %}{% else %} | {% endif %}{% endfor %}</p>
+    {% endif %}
+    <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">Track</th>
+                        <th scope="col">Album</th>
+                    </tr>
+                </thead>
+                <tbody>
+                    {% for scrobble in object.scrobble_set.all %}
+                    <tr>
+                        <td>{{scrobble.timestamp}}</td>
+                        <td>{{scrobble.track.title}}</td>
+                        <td>{{scrobble.track.album.name}}</td>
+                    </tr>
+                    {% endfor %}
+                </tbody>
+            </table>
+        </div>
+    </div>
+</div>
 
 {% endblock %}

+ 100 - 73
vrobbler/templates/scrobbles/chart_index.html

@@ -5,6 +5,30 @@
 {% block lists %}
 <div class="row">
     {% if charts %}
+    {% if "Artist" in name %}
+    <div class="tab-content" id="artistTabContent">
+        <div class="table-responsive">
+            <table class="table table-striped table-sm">
+                <thead>
+                    <tr>
+                        <th scope="col">Rank</th>
+                        <th scope="col">Artist</th>
+                        <th scope="col">Scrobbles</th>
+                    </tr>
+                </thead>
+                <tbody>
+                    {% for chart in charts %}
+                    <tr>
+                        <td>{{chart.rank}}</td>
+                        <td><a href="{{chart.media_obj.get_absolute_url}}">{{chart.media_obj}}</a></td>
+                        <td>{{chart.count}}</td>
+                    </tr>
+                    {% endfor %}
+                </tbody>
+            </table>
+        </div>
+    </div>
+    {% elif media_type == "Track" %}
     <div class="tab-content" id="artistTabContent">
         <div class="table-responsive">
             <table class="table table-striped table-sm">
@@ -29,93 +53,96 @@
             </table>
         </div>
     </div>
+    {% elif "Video" in name %}
+    {% elif "Series" in name %}
+    {% endif %}
     {% endif %}
 
     {% if artist_charts %}
-    <h2>Top Artists</h2>
+    <div class="col-md">
+        <h2>Top Artists</h2>
 
-    <ul class="nav nav-tabs" id="artistTab" role="tablist">
-        {% for chart_name in artist_charts.keys %}
-        <li class="nav-item" role="presentation">
-            <button class="nav-link {% if forloop.first %}active{% endif %}" id="artist-{{chart_name}}-tab" data-bs-toggle="tab" data-bs-target="#artist-{{chart_name}}"
-                    type="button" role="tab" aria-controls="home" aria-selected="true">
-                {{chart_name}}
-            </button>
-        </li>
-        {% endfor %}
-    </ul>
+        <ul class="nav nav-tabs" id="artistTab" role="tablist">
+            {% for chart_name in artist_charts.keys %}
+            <li class="nav-item" role="presentation">
+                <button class="nav-link {% if forloop.first %}active{% endif %}" id="artist-{{chart_name}}-tab" data-bs-toggle="tab" data-bs-target="#artist-{{chart_name}}"
+                        type="button" role="tab" aria-controls="home" aria-selected="true">
+                    {{chart_name}}
+                </button>
+            </li>
+            {% endfor %}
+        </ul>
 
-    <div class="tab-content" id="artistTabContent">
-        {% for chart_name, artists in artist_charts.items %}
-        <div class="tab-pane fade {% if forloop.first %}show active{% endif %}" id="artist-{{chart_name}}" role="tabpanel"
-            aria-labelledby="artist-{[chart}}-tab">
-            <div class="table-responsive">
-                <table class="table table-striped table-sm">
-                    <thead>
-                        <tr>
-                            <th scope="col">Artist</th>
-                            <th scope="col">Scrobbles</th>
-                        </tr>
-                    </thead>
-                    <tbody>
-                        {% for artist in artists %}
-                        <tr>
-                            <td><a href="{{artist.get_absolute_url}}">{{artist}}</a></td>
-                            <td>{{artist.num_scrobbles}}</td>
-                        </tr>
-                        {% endfor %}
-                    </tbody>
-                </table>
+        <div class="tab-content" id="artistTabContent">
+            {% for chart_name, artists in artist_charts.items %}
+            <div class="tab-pane fade {% if forloop.first %}show active{% endif %}" id="artist-{{chart_name}}" role="tabpanel"
+                aria-labelledby="artist-{[chart}}-tab">
+                <div class="table-responsive">
+                    <table class="table table-striped table-sm">
+                        <thead>
+                            <tr>
+                                <th scope="col">Artist</th>
+                                <th scope="col">Scrobbles</th>
+                            </tr>
+                        </thead>
+                        <tbody>
+                            {% for artist in artists %}
+                            <tr>
+                                <td><a href="{{artist.get_absolute_url}}">{{artist}}</a></td>
+                                <td>{{artist.num_scrobbles}}</td>
+                            </tr>
+                            {% endfor %}
+                        </tbody>
+                    </table>
+                </div>
             </div>
+            {% endfor %}
         </div>
-        {% endfor %}
+        {% endif %}
     </div>
-    {% endif %}
-</div>
 
-<div class="row">
     {% if track_charts %}
-    <h2>Top Tracks</h2>
+    <div class="col-md">
+        <h2>Top Tracks</h2>
 
-    <ul class="nav nav-tabs" id="artistTab" role="tablist">
-        {% for chart_name in track_charts.keys %}
-        <li class="nav-item" role="presentation">
-            <button class="nav-link {% if forloop.first %}active{% endif %}" id="track-{{chart_name}}-tab" data-bs-toggle="tab" data-bs-target="#track-{{chart_name}}"
-                    type="button" role="tab" aria-controls="home" aria-selected="true">
-                {{chart_name}}
-            </button>
-        </li>
-        {% endfor %}
-    </ul>
+        <ul class="nav nav-tabs" id="artistTab" role="tablist">
+            {% for chart_name in track_charts.keys %}
+            <li class="nav-item" role="presentation">
+                <button class="nav-link {% if forloop.first %}active{% endif %}" id="track-{{chart_name}}-tab" data-bs-toggle="tab" data-bs-target="#track-{{chart_name}}"
+                        type="button" role="tab" aria-controls="home" aria-selected="true">
+                    {{chart_name}}
+                </button>
+            </li>
+            {% endfor %}
+        </ul>
 
-    <div class="tab-content" id="trackTabContent">
-        {% for chart_name, tracks in track_charts.items %}
-        <div class="tab-pane fade {% if forloop.first %}show active{% endif %}" id="track-{{chart_name}}" role="tabpanel"
-            aria-labelledby="track-{[chart_name}}-tab">
-            <div class="table-responsive">
-                <table class="table table-striped table-sm">
-                    <thead>
-                        <tr>
-                            <th scope="col">Rank</th>
-                            <th scope="col">Artist</th>
-                            <th scope="col">Track</th>
-                            <th scope="col">Scrobbles</th>
-                        </tr>
-                    </thead>
-                    <tbody>
-                        {% for track in tracks %}
-                        <tr>
-                            <td>{{track.rank}}</td>
-                            <td><a href="{{track.artist.get_absolute_url}}">{{track.artist}}</a></td>
-                            <td><a href="{{track.get_absolute_url}}">{{track.title}}</a></td>
-                            <td>{{track.num_scrobbles}}</td>
-                        </tr>
-                        {% endfor %}
-                    </tbody>
-                </table>
+        <div class="tab-content" id="trackTabContent">
+            {% for chart_name, tracks in track_charts.items %}
+            <div class="tab-pane fade {% if forloop.first %}show active{% endif %}" id="track-{{chart_name}}" role="tabpanel"
+                aria-labelledby="track-{[chart_name}}-tab">
+                <div class="table-responsive">
+                    <table class="table table-striped table-sm">
+                        <thead>
+                            <tr>
+                                <th scope="col">Track</th>
+                                <th scope="col">Artist</th>
+                                <th scope="col">Scrobbles</th>
+                            </tr>
+                        </thead>
+                        <tbody>
+                            {% for track in tracks %}
+                            <tr>
+                                <td><a href="{{track.get_absolute_url}}">{{track.title}}</a></td>
+                                <td><a href="{{track.artist.get_absolute_url}}">{{track.artist}}</a></td>
+                                <td>{{track.num_scrobbles}}</td>
+                            </tr>
+                            {% endfor %}
+                        </tbody>
+                    </table>
+                </div>
             </div>
+            {% endfor %}
         </div>
-        {% endfor %}
     </div>
     {% endif %}
 </div>