[templates] Allow going back and forward in time

This commit is contained in:
2025-06-07 15:17:56 -04:00
parent 99a6e5107b
commit 69aa80e6c1
6 changed files with 219 additions and 324 deletions

View File

@ -604,45 +604,39 @@ class Scrobble(TimeStampedModel):
@classmethod @classmethod
def for_year(cls, user, year): def for_year(cls, user, year):
return cls.objects.filter(timestamp__year=year, user=user) return cls.objects.filter(timestamp__year=year, user=user).order_by(
"-timestamp"
)
@classmethod @classmethod
def for_month(cls, user, year, month): def for_month(cls, user, year, month):
return cls.objects.filter( return cls.objects.filter(
timestamp__year=year, timestamp__month=month, user=user timestamp__year=year, timestamp__month=month, user=user
) ).order_by("-timestamp")
@classmethod @classmethod
def for_day(cls, user, year, day, month): def for_day(cls, user, year, month, day):
return cls.objects.filter( return cls.objects.filter(
timestamp__year=year, timestamp__year=year,
timestamp__month=month, timestamp__month=month,
timestamp__day=day, timestamp__day=day,
user=user, user=user,
)
@classmethod
def for_day_dict(
cls, user_id: int, year: int, month: int, day: int
) -> dict:
scrobbles_by_type = defaultdict(list)
scrobbles = cls.objects.filter(
timestamp__year=year,
timestamp__month=month,
timestamp__day=day,
user_id=user_id,
).order_by("-timestamp") ).order_by("-timestamp")
for scrobble in scrobbles:
scrobbles_by_type[scrobble.media_type].append(scrobble)
return scrobbles_by_type
@classmethod @classmethod
def for_week(cls, user, year, week): def for_week(cls, user, year, week):
return cls.objects.filter( return cls.objects.filter(
timestamp__year=year, timestamp__week=week, user=user timestamp__year=year, timestamp__week=week, user=user
) ).order_by("-timestamp")
@classmethod
def as_dict_by_type(cls, scrobble_qs: models.QuerySet) -> dict:
scrobbles_by_type = defaultdict(list)
for scrobble in scrobble_qs:
scrobbles_by_type[scrobble.media_type].append(scrobble)
return scrobbles_by_type
@classmethod @classmethod
def in_progress_for_user(cls, user_id: int) -> models.QuerySet: def in_progress_for_user(cls, user_id: int) -> models.QuerySet:
@ -847,7 +841,10 @@ class Scrobble(TimeStampedModel):
@property @property
def elapsed_time(self) -> int | None: def elapsed_time(self) -> int | None:
if self.played_to_completion: if self.played_to_completion:
return self.playback_position_seconds if self.playback_position_seconds:
return self.playback_position_seconds
if self.media_obj.run_time_seconds:
return self.media_obj.run_time_seconds
return (timezone.now() - self.timestamp).seconds return (timezone.now() - self.timestamp).seconds
@property @property

View File

@ -2,9 +2,9 @@ import calendar
import json import json
import logging import logging
from datetime import datetime, timedelta from datetime import datetime, timedelta
from dateutil.relativedelta import relativedelta
import pendulum import pendulum
from pendulum.parsing.exceptions import ParserError
import pytz import pytz
from django.apps import apps from django.apps import apps
from django.contrib import messages from django.contrib import messages
@ -109,20 +109,92 @@ class RecentScrobbleList(ListView):
user = self.request.user user = self.request.user
data["date"] = "" data["date"] = ""
if user.is_authenticated: if user.is_authenticated:
date = self.request.GET.get("date", "") self.queryset = self.get_queryset().filter(user=user)
today = timezone.localtime(timezone.now())
user_id = self.request.user.id user_id = self.request.user.id
year = today.year
month = today.month today = timezone.localtime(timezone.now())
day = today.day date_str = self.request.GET.get("date", "")
if date: date = today
if date_str:
try: try:
data["date"] = pendulum.parse(date) date = pendulum.parse(date_str)
year, month, day = date.split("-")
except: except:
pass pass
if date_str:
data = data | Scrobble.for_day_dict(user_id, year, month, day) if date_str == "this_week" or "-W" in date_str:
next_date = date + timedelta(weeks=+2)
prev_date = date + timedelta(weeks=-1)
if date.isocalendar()[1] < today.isocalendar()[1]:
data[
"next_link"
] = f"?date={next_date.strftime('%Y-W%W')}"
data["prev_link"] = f"?date={prev_date.strftime('%Y-W%W')}"
data[
"title"
] = f"Week {date.strftime('%-W')} of {date.year}"
data = data | Scrobble.as_dict_by_type(
Scrobble.for_week(
user_id, date.year, date.isocalendar()[1]
)
)
elif date_str == "this_month" or date_str.count("-") == 1:
next_date = date + relativedelta(months=1)
prev_date = date + relativedelta(months=-1)
if date.month < today.month:
data[
"next_link"
] = f"?date={next_date.strftime('%Y-%m')}"
data["title"] = f"{date.strftime('%B %Y')}"
data["prev_link"] = f"?date={prev_date.strftime('%Y-%m')}"
data = data | Scrobble.as_dict_by_type(
Scrobble.for_month(user_id, date.year, date.month)
)
elif date_str == "today" or date_str.count("-") == 2:
next_date = date + timedelta(days=1)
prev_date = date - timedelta(days=1)
data[
"prev_link"
] = f"?date={prev_date.strftime('%Y-%m-%d')}"
if date < today:
data[
"next_link"
] = f"?date={next_date.strftime('%Y-%m-%d')}"
if date == today:
data["title"] = "Today"
else:
data["title"] = f"{date.strftime('%Y-%m-%d')}"
data[
"today_link"
] = f"?date={today.strftime('%Y-%m-%d')}"
data = data | Scrobble.as_dict_by_type(
Scrobble.for_day(
user_id, date.year, date.month, date.day
)
)
elif date_str == "this_year" or date_str.count("-") == 0:
next_date = date + relativedelta(years=+1)
prev_date = date + relativedelta(years=-1)
data["next_link"] = ""
if date.year < today.year:
data["title"] = f"{date.strftime('%Y')}"
data["next_link"] = f"?date={next_date.year}"
data["title"] = f"{date.year}"
data["prev_link"] = f"?date={prev_date.year}"
data = data | Scrobble.as_dict_by_type(
Scrobble.for_year(user_id, date.year)
)
else:
next_date = today - timedelta(days=1)
prev_date = today - timedelta(days=1)
data["title"] = "Today"
data["next_link"] = ""
data["prev_link"] = f"?date={prev_date.strftime('%Y-%m-%d')}"
data = data | Scrobble.as_dict_by_type(
Scrobble.for_day(user_id, date.year, date.month, date.day)
)
data[
"today_link"
] = "" # f"?date={today.strftime('%Y-%m-%d')}"
data["active_imports"] = AudioScrobblerTSVImport.objects.filter( data["active_imports"] = AudioScrobblerTSVImport.objects.filter(
processing_started__isnull=False, processing_started__isnull=False,
@ -139,9 +211,7 @@ class RecentScrobbleList(ListView):
return data return data
def get_queryset(self): def get_queryset(self):
return Scrobble.objects.filter( return Scrobble.objects.all().order_by("-timestamp")
track__isnull=False, in_progress=False
).order_by("-timestamp")[:15]
class ScrobbleLongPlaysView(TemplateView): class ScrobbleLongPlaysView(TemplateView):
@ -453,6 +523,7 @@ def gps_webhook(request):
return Response({"scrobble_id": scrobble.id}, status=status.HTTP_200_OK) return Response({"scrobble_id": scrobble.id}, status=status.HTTP_200_OK)
@csrf_exempt @csrf_exempt
@api_view(["POST"]) @api_view(["POST"])
def emacs_webhook(request): def emacs_webhook(request):
@ -474,9 +545,9 @@ def emacs_webhook(request):
if request.user.id: if request.user.id:
user_id = request.user.id user_id = request.user.id
#scrobble = gpslogger_scrobble_location(data_dict, user_id) # scrobble = gpslogger_scrobble_location(data_dict, user_id)
#if not scrobble: # if not scrobble:
# return Response({}, status=status.HTTP_200_OK) # return Response({}, status=status.HTTP_200_OK)
return Response({"post_data": post_data}, status=status.HTTP_200_OK) return Response({"post_data": post_data}, status=status.HTTP_200_OK)

View File

@ -1,305 +1,82 @@
{% load humanize %} {% load humanize %}
{% load naturalduration %} {% load naturalduration %}
<div> <div class="row">
<p>Today <b>{{counts.today}}</b> | This Week <b>{{counts.week}}</b> | This Month <b>{{counts.month}}</b> | This Year <b>{{counts.year}}</b> | All Time <b>{{counts.alltime}}</b></p> <div>
<p>Today <b>{{counts.today}}</b> | This Week <b>{{counts.week}}</b> | This Month <b>{{counts.month}}</b> | This Year <b>{{counts.year}}</b> | All Time <b>{{counts.alltime}}</b></p>
</div>
</div> </div>
<div class="row"> <div class="row">
<div class="col-md">
{% if Track %} {% if Track %}
<div class="ab-pane fade show active" id="latest-listened" role="tabpanel" <h2>Music</h2>
aria-labelledby="latest-listened-tab"> {% with scrobbles=Track %}
<div class="table-responsive"> {% include "scrobbles/_scrobble_table.html" %}
<table class="table table-striped table-sm"> {% endwith %}
<thead> {% endif %}
<tr> </div>
<th scope="col">Time</th> <div class="col-md">
<th scope="col">Album</th>
<th scope="col">Track</th> {% if Task %}
<th scope="col">Artist</th> <h2>Latest tasks</h2>
</tr> {% with scrobbles=Task %}
</thead> {% include "scrobbles/_scrobble_table.html" %}
<tbody> {% endwith %}
{{Track|length}}
{% for scrobble in Track %}
<tr>
<td>{% if scrobble.in_progress %}{{scrobble.media_obj.strings.verb}} now | <a class="right" href="{% url "scrobbles:finish" scrobble.uuid %}">Finish</a>{% else %}{{scrobble.timestamp|naturaltime}}{% endif %}</td>
{% if scrobble.track.album.cover_image %}
<td><a href="{{scrobble.track.album.get_absolute_url}}"><img src="{{scrobble.track.album.cover_image_small.url}}" width=25 height=25 style="border:1px solid black;" /></aa></td>
{% else %}
<td><a href="{{scrobble.track.album.get_absolute_url}}">{{scrobble.track.album.name}}</a></td>
{% endif %}
<td><a href="{{scrobble.track.get_absolute_url }}">{{scrobble.track.title}}</a></td>
<td><a href="{{scrobble.track.artist.get_absolute_url }}">{{scrobble.track.artist.name}}</aa></td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endif %} {% endif %}
{% if Video %} {% if Video %}
<div class="tab-pane fade show" id="latest-watched" role="tabpanel" <h2>Videos</h2>
aria-labelledby="latest-watched-tab"> {% with scrobbles=Video %}
<div class="table-responsive"> {% include "scrobbles/_scrobble_table.html" %}
<table class="table table-striped table-sm"> {% endwith %}
<thead>
<tr>
<th scope="col">Time</th>
<th scope="col">Cover</th>
<th scope="col">Title</th>
<th scope="col">Series</th>
</tr>
</thead>
<tbody>
{% for scrobble in Video %}
<tr>
<td>{% if scrobble.in_progress %}{{scrobble.media_obj.strings.verb}} now | <a class="right" href="{% url "scrobbles:finish" scrobble.uuid %}">Finish</a>{% else %}{{scrobble.timestamp|naturaltime}}{% endif %}</td>
{% if scrobble.video.cover_image %}
<td><img src="{{scrobble.media_obj.cover_image_medium.url}}" width=25 height=25 style="border:1px solid black;" /></td>
{% else %}
<td></td>
{% endif %}
<td><a href="{{scrobble.video.get_absolute_url }}">{% if scrobble.video.tv_series%}S{{scrobble.video.season_number}}E{{scrobble.video.episode_number}} -{%endif %} {{scrobble.video.title}}</a></td>
<td><a href="{{scrobble.video.tv_series.get_absolute_url }}">{% if scrobble.video.tv_series %}{{scrobble.video.tv_series}}</a>{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endif %}
{% if SportEvent %}
<div class="tab-pane fade show" id="latest-sports" role="tabpanel" aria-labelledby="latest-sports-tab">
<h2>Latest Sports</h2>
<div class="table-responsive">
<table class="table table-striped table-sm">
<thead>
<tr>
<th scope="col">Date</th>
<th scope="col">Title</th>
<th scope="col">Round</th>
<th scope="col">League</th>
<th scope="col">Time watched</th>
</tr>
</thead>
<tbody>
{% for scrobble in SportEvent %}
<tr>
<td>{% if scrobble.in_progress %}{{scrobble.media_obj.strings.verb}} now | <a class="right" href="{% url "scrobbles:finish" scrobble.uuid %}">Finish</a>{% else %}{{scrobble.timestamp|naturaltime}}{% endif %}</td>
<td>{{scrobble.sport_event.title}}</td>
<td>{{scrobble.sport_event.round.name}}</td>
<td>{{scrobble.sport_event.round.season.league}}</td>
<td>{{scrobble.elapsed_time|natural_duration}}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endif %}
{% if PodcastEpisode %}
<div class="tab-pane fade show" id="latest-podcasted" role="tabpanel"
aria-labelledby="latest-podcasted-tab">
<div class="table-responsive">
<table class="table table-striped table-sm">
<thead>
<tr>
<th scope="col">Date</th>
<th scope="col">Title</th>
<th scope="col">Podcast</th>
</tr>
</thead>
<tbody>
{% for scrobble in PodcastEpisode %}
<tr>
<td>{% if scrobble.in_progress %}{{scrobble.media_obj.strings.verb}} now | <a class="right" href="{% url "scrobbles:finish" scrobble.uuid %}">Finish</a>{% else %}{{scrobble.timestamp|naturaltime}}{% endif %}</td>
<td>{{scrobble.timestamp|naturaltime}}</td>
<td>{{scrobble.podcast_episode.title}}</td>
<td>{{scrobble.podcast_episode.podcast}}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endif %}
{% if VideoGame %}
<h4>Video games</h4>
<div class="tab-pane fade show" id="latest-videogames" role="tabpanel"
aria-labelledby="latest-videogames-tab">
<div class="table-responsive">
<table class="table table-striped table-sm">
<thead>
<tr>
<th scope="col">Date</th>
<th scope="col">Cover</th>
<th scope="col">Title</th>
<th scope="col">Time played</th>
<th scope="col">Percent complete</th>
</tr>
</thead>
<tbody>
{% for scrobble in VideoGame %}
<tr>
<td>{% if scrobble.in_progress %}{{scrobble.media_obj.strings.verb}} now | <a class="right" href="{% url "scrobbles:finish" scrobble.uuid %}">Finish</a>{% else %}{{scrobble.timestamp|naturaltime}}{% endif %}</td>
{% if scrobble.media_obj.hltb_cover %}
<td><img src="{{scrobble.media_obj.hltb_cover_medium.url}}" width=25 height=25 style="border:1px solid black;" /></td>
{% endif %}
<td><a href="{{scrobble.media_obj.get_absolute_url}}">{{scrobble.media_obj.title}}</a></td>
<td>{{scrobble.elapsed_time|natural_duration}}</td>
<td>{{scrobble.percent_played}}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endif %}
{% if BoardGame %}
<h4>Board games</h4>
<div class="tab-pane fade show" id="latest-boardgames" role="tabpanel"
aria-labelledby="latest-boardgames-tab">
<div class="table-responsive">
<table class="table table-striped table-sm">
<thead>
<tr>
<th scope="col">Date</th>
<th scope="col">Cover</th>
<th scope="col">Title</th>
<th scope="col">Time played</th>
</tr>
</thead>
<tbody>
{% for scrobble in BoardGame %}
<tr>
<td>{% if scrobble.in_progress %}{{scrobble.media_obj.strings.verb}} now | <a class="right" href="{% url "scrobbles:finish" scrobble.uuid %}">Finish</a>{% else %}{{scrobble.timestamp|naturaltime}}{% endif %}</td>
<td><img src="{{scrobble.media_obj.cover_medium.url}}" width=25 height=25 style="border:1px solid black;" /></td>
<td><a href="{{scrobble.media_obj.get_absolute_url}}">{{scrobble.media_obj.title}}</a></td>
<td>{{scrobble.elapsed_time|natural_duration}}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endif %} {% endif %}
{% if WebPage %} {% if WebPage %}
<h4>Web pages</h4> <h4>Web pages</h4>
<div class="tab-pane fade show" id="latest-webpages" role="tabpanel" {% with scrobbles=WebPage %}
aria-labelledby="latest-webpages-tab"> {% include "scrobbles/_scrobble_table.html" %}
<div class="table-responsive"> {% endwith %}
<table class="table table-striped table-sm"> {% endif %}
<thead>
<tr> {% if SportEvent %}
<th scope="col">Date</th> <h2>Sports</h2>
<th scope="col">Title</th> {% with scrobbles=SportEvent %}
<th scope="col">Time browsing</th> {% include "scrobbles/_scrobble_table.html" %}
</tr> {% endwith %}
</thead> {% endif %}
<tbody>
{% for scrobble in WebPage %} {% if PodcastEpisode %}
<tr> <h2>Latest podcasts</h2>
<td>{% if scrobble.in_progress %}{{scrobble.media_obj.strings.verb}} now | <a class="right" href="{% url "scrobbles:finish" scrobble.uuid %}">Finish</a>{% else %}{{scrobble.timestamp|naturaltime}}{% endif %}</td> {% with scrobbles=PodcastEpisode %}
<td><a href="{{scrobble.media_obj.get_absolute_url}}">{{scrobble.media_obj.title}}</a></td> {% include "scrobbles/_scrobble_table.html" %}
<td>{{scrobble.elapsed_time|natural_duration}}</td> {% endwith %}
</tr> {% endif %}
{% endfor %}
</tbody> {% if VideoGame %}
</table> <h4>Video games</h4>
</div> {% with scrobbles=VideoGame %}
</div> {% include "scrobbles/_scrobble_table.html" %}
{% endwith %}
{% endif %}
{% if BoardGame %}
<h4>Board games</h4>
{% with scrobbles=BoardGame %}
{% include "scrobbles/_scrobble_table.html" %}
{% endwith %}
{% endif %} {% endif %}
{% if Beer %} {% if Beer %}
<h4>Beers</h4> <h4>Beers</h4>
<div class="tab-pane fade show" id="latest-beers" role="tabpanel" {% with scrobbles=Beer %}
aria-labelledby="latest-beers-tab"> {% include "scrobbles/_scrobble_table.html" %}
<div class="table-responsive"> {% endwith %}
<table class="table table-striped table-sm">
<thead>
<tr>
<th scope="col">Date</th>
<th scope="col">Cover</th>
<th scope="col">Title</th>
<th scope="col">Time drinking</th>
</tr>
</thead>
<tbody>
{% for scrobble in Beer %}
<tr>
<td>{% if scrobble.in_progress %}{{scrobble.media_obj.strings.verb}} now | <a class="right" href="{% url "scrobbles:finish" scrobble.uuid %}">Finish</a>{% else %}{{scrobble.timestamp|naturaltime}}{% endif %}</td>
<td><img src="{{scrobble.media_obj.cover_medium.url}}" width=25 height=25 style="border:1px solid black;" /></td>
<td><a href="{{scrobble.media_obj.get_absolute_url}}">{{scrobble.media_obj.title}}</a></td>
<td>{{scrobble.elapsed_time|natural_duration}}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endif %} {% endif %}
{% if Book %} {% if Book %}
<h4>Books</h4> <h4>Books</h4>
<div class="tab-pane fade show" id="latest-books" role="tabpanel" {% with scrobbles=Book %}
aria-labelledby="latest-books-tab"> {% include "scrobbles/_scrobble_table.html" %}
<div class="table-responsive"> {% endwith %}
<table class="table table-striped table-sm">
<thead>
<tr>
<th scope="col">Date</th>
<th scope="col">Cover</th>
<th scope="col">Title</th>
<th scope="col">Time reading</th>
</tr>
</thead>
<tbody>
{% for scrobble in Book %}
<tr>
<td>{% if scrobble.in_progress %}{{scrobble.media_obj.strings.verb}} now | <a class="right" href="{% url "scrobbles:finish" scrobble.uuid %}">Finish</a>{% else %}{{scrobble.timestamp|naturaltime}}{% endif %}</td>
<td><img src="{scrobble.media_obj.cover_medium.url}}" width=25 height=25 style="border:1px solid black;" /></td>
<td><a href="{{scrobble.media_obj.get_absolute_url}}">{{scrobble.media_obj.title}}</a></td>
<td>{{scrobble.elapsed_time|natural_duration}}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endif %}
{% if Task %}
<div class="tab-pane fade show" id="latest-tasks" role="tabpanel"
aria-labelledby="latest-tasks-tab">
<div class="table-responsive">
<table class="table table-striped table-sm">
<thead>
<tr>
<th scope="col">Date</th>
<th scope="col">Title</th>
<th scope="col">Time doing</th>
</tr>
</thead>
<tbody>
{% for scrobble in Task %}
<tr>
<td>{% if scrobble.in_progress %}{{scrobble.media_obj.strings.verb}} now | <a class="right" href="{% url "scrobbles:finish" scrobble.uuid %}">Finish</a>{% else %}{{scrobble.timestamp|naturaltime}}{% endif %}</td>
<td><a href="{{scrobble.media_obj.get_absolute_url}}">{{scrobble.media_obj.title}}</a></td>
<td>{{scrobble.elapsed_time|natural_duration}}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endif %} {% endif %}
</div> </div>

View File

@ -0,0 +1,7 @@
{% load humanize %}
{% load naturalduration %}
<tr>
<td>{% if scrobble.in_progress %}{{scrobble.media_obj.strings.verb}} now | <a class="right" href="{% url "scrobbles:finish" scrobble.uuid %}">Finish</a>{% else %}{{scrobble.timestamp|naturaltime}}{% endif %}</td>
<td><a href="{{scrobble.media_obj.get_absolute_url}}">{{scrobble.media_obj|truncatechars_html:50}}</a></td>
<td>{{scrobble.elapsed_time|natural_duration}}</td>
</tr>

View File

@ -0,0 +1,22 @@
{% load humanize %}
{% load naturalduration %}
<div class="tab-pane fade show" id="latest-beers" role="tabpanel"
aria-labelledby="latest-beers-tab">
<div class="table-responsive">
<table class="table table-striped table-sm">
<thead>
<tr>
<th scope="col">Date</th>
<th scope="col">Title</th>
<th scope="col">Time</th>
</tr>
</thead>
<tbody>
{% for scrobble in scrobbles %}
{% include "scrobbles/_row.html" %}
{% endfor %}
</tbody>
</table>
</div>
</div>

View File

@ -49,9 +49,28 @@
<main class="col-md-9 ms-sm-auto col-lg-10 px-md-4"> <main class="col-md-9 ms-sm-auto col-lg-10 px-md-4">
<div <div
class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom"> class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
<h1 class="h2">{% if date %}{{date|naturaltime}}{% else %}Today{% endif %}</h1> <h1 class="h2">{% if date %}{{date|naturaltime}}{% else %}{{title}}{% endif %}</h1>
<div class="btn-toolbar mb-2 mb-md-0"> <div class="btn-toolbar mb-2 mb-md-0">
{% if user.is_authenticated %} {% if user.is_authenticated %}
<div class="btn-group me-2">
{% if user.profile.lastfm_username and not user.profile.lastfm_auto_import %}
<form action="{% url 'scrobbles:lastfm-import' %}" method="get">
<button type="submit" class="btn btn-sm btn-outline-secondary">Last.fm Sync</button>
</form>
{% endif %}
{% if prev_link %}
<a type="button" class="btn btn-sm btn-outline-secondary" href="{{prev_link}}"
data-bs-target="#">Previous</a>
{% endif %}
{% if today_link %}
<a type="button" class="btn btn-sm btn-outline-secondary" href="{{today_link}}"
data-bs-target="#">Today</a>
{% endif %}
{% if next_link %}
<a type="button" class="btn btn-sm btn-outline-secondary" href="{{next_link}}"
data-bs-target="#">Next</a>
{% endif %}
</div>
<div class="btn-group me-2"> <div class="btn-group me-2">
{% if user.profile.lastfm_username and not user.profile.lastfm_auto_import %} {% if user.profile.lastfm_username and not user.profile.lastfm_auto_import %}
@ -69,11 +88,13 @@
<button type="button" class="btn btn-sm btn-outline-secondary dropdown-toggle" id="graphDateButton" <button type="button" class="btn btn-sm btn-outline-secondary dropdown-toggle" id="graphDateButton"
data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<div data-feather="calendar"></div> <div data-feather="calendar"></div>
This week {{title}}
</button> </button>
<div class="dropdown-menu" data-bs-toggle="#graphDataChange" aria-labelledby="graphDateButton"> <div class="dropdown-menu" data-bs-toggle="#graphDataChange" aria-labelledby="graphDateButton">
<a class="dropdown-item" href="#">This month</a> <a class="dropdown-item" href="?date=today">Today</a>
<a class="dropdown-item" href="#">This year</a> <a class="dropdown-item" href="?date=this_week">This week</a>
<a class="dropdown-item" href="?date=this_month">This month</a>
<a class="dropdown-item" href="?date=this_year">This year</a>
</div> </div>
</div> </div>
</div> </div>