[templates] Start redesigning the dashboard
This commit is contained in:
@ -2,6 +2,7 @@ import calendar
|
|||||||
import datetime
|
import datetime
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
|
from collections import defaultdict
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
|
|
||||||
@ -514,6 +515,10 @@ class Scrobble(TimeStampedModel):
|
|||||||
MOOD = "Mood", "Mood"
|
MOOD = "Mood", "Mood"
|
||||||
BRICKSET = "BrickSet", "Brick set"
|
BRICKSET = "BrickSet", "Brick set"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def list(cls):
|
||||||
|
return list(map(lambda c: c.value, cls))
|
||||||
|
|
||||||
uuid = models.UUIDField(editable=False, **BNULL)
|
uuid = models.UUIDField(editable=False, **BNULL)
|
||||||
video = models.ForeignKey(Video, on_delete=models.DO_NOTHING, **BNULL)
|
video = models.ForeignKey(Video, on_delete=models.DO_NOTHING, **BNULL)
|
||||||
track = models.ForeignKey(Track, on_delete=models.DO_NOTHING, **BNULL)
|
track = models.ForeignKey(Track, on_delete=models.DO_NOTHING, **BNULL)
|
||||||
@ -597,6 +602,57 @@ class Scrobble(TimeStampedModel):
|
|||||||
long_play_seconds = models.BigIntegerField(**BNULL)
|
long_play_seconds = models.BigIntegerField(**BNULL)
|
||||||
long_play_complete = models.BooleanField(**BNULL)
|
long_play_complete = models.BooleanField(**BNULL)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def for_year(cls, user, year):
|
||||||
|
return cls.objects.filter(timestamp__year=year, user=user)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def for_month(cls, user, year, month):
|
||||||
|
return cls.objects.filter(
|
||||||
|
timestamp__year=year, timestamp__month=month, user=user
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def for_day(cls, user, year, day, month):
|
||||||
|
return cls.objects.filter(
|
||||||
|
timestamp__year=year,
|
||||||
|
timestamp__month=month,
|
||||||
|
timestamp__day=day,
|
||||||
|
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")
|
||||||
|
|
||||||
|
for scrobble in scrobbles:
|
||||||
|
scrobbles_by_type[scrobble.media_type].append(scrobble)
|
||||||
|
|
||||||
|
return scrobbles_by_type
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def for_week(cls, user, year, week):
|
||||||
|
return cls.objects.filter(
|
||||||
|
timestamp__year=year, timestamp__week=week, user=user
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def in_progress_for_user(cls, user_id: int) -> models.QuerySet:
|
||||||
|
return cls.objects.filter(
|
||||||
|
user=user_id,
|
||||||
|
in_progress=True,
|
||||||
|
played_to_completion=False,
|
||||||
|
is_paused=False,
|
||||||
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def last_serial_scrobble(self) -> Optional["Scrobble"]:
|
def last_serial_scrobble(self) -> Optional["Scrobble"]:
|
||||||
from scrobbles.models import Scrobble
|
from scrobbles.models import Scrobble
|
||||||
|
|||||||
@ -3,9 +3,9 @@ import json
|
|||||||
import logging
|
import logging
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
|
import pendulum
|
||||||
import pytz
|
import pytz
|
||||||
from django.apps import apps
|
from django.apps import apps
|
||||||
from django.conf import settings
|
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
from django.db.models import Count, Q
|
from django.db.models import Count, Q
|
||||||
@ -52,7 +52,6 @@ from scrobbles.tasks import (
|
|||||||
from scrobbles.utils import (
|
from scrobbles.utils import (
|
||||||
get_long_plays_completed,
|
get_long_plays_completed,
|
||||||
get_long_plays_in_progress,
|
get_long_plays_in_progress,
|
||||||
get_recently_played_board_games,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@ -107,32 +106,19 @@ class RecentScrobbleList(ListView):
|
|||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
data = super().get_context_data(**kwargs)
|
data = super().get_context_data(**kwargs)
|
||||||
user = self.request.user
|
user = self.request.user
|
||||||
|
data["date"] = ""
|
||||||
if user.is_authenticated:
|
if user.is_authenticated:
|
||||||
|
date = self.request.GET.get("date", "")
|
||||||
|
today = timezone.localtime(timezone.now())
|
||||||
|
user_id = self.request.user.id
|
||||||
|
year = today.year
|
||||||
|
month = today.month
|
||||||
|
day = today.day
|
||||||
|
if date:
|
||||||
|
year, month, day = date.split("-")
|
||||||
|
data["date"] = pendulum.parse(date)
|
||||||
|
|
||||||
completed_for_user = Scrobble.objects.filter(
|
data = data | Scrobble.for_day_dict(user_id, year, month, day)
|
||||||
played_to_completion=True, user=user
|
|
||||||
)
|
|
||||||
data["long_play_in_progress"] = get_long_plays_in_progress(user)
|
|
||||||
data["play_again"] = get_recently_played_board_games(user)
|
|
||||||
data["video_scrobble_list"] = completed_for_user.filter(
|
|
||||||
video__isnull=False
|
|
||||||
).order_by("-timestamp")[:15]
|
|
||||||
|
|
||||||
data["podcast_scrobble_list"] = completed_for_user.filter(
|
|
||||||
podcast_episode__isnull=False
|
|
||||||
).order_by("-timestamp")[:15]
|
|
||||||
|
|
||||||
data["sport_scrobble_list"] = completed_for_user.filter(
|
|
||||||
sport_event__isnull=False
|
|
||||||
).order_by("-timestamp")[:15]
|
|
||||||
|
|
||||||
data["videogame_scrobble_list"] = completed_for_user.filter(
|
|
||||||
video_game__isnull=False
|
|
||||||
).order_by("-timestamp")[:15]
|
|
||||||
|
|
||||||
data["boardgame_scrobble_list"] = completed_for_user.filter(
|
|
||||||
board_game__isnull=False
|
|
||||||
).order_by("-timestamp")[:15]
|
|
||||||
|
|
||||||
data["active_imports"] = AudioScrobblerTSVImport.objects.filter(
|
data["active_imports"] = AudioScrobblerTSVImport.objects.filter(
|
||||||
processing_started__isnull=False,
|
processing_started__isnull=False,
|
||||||
@ -698,45 +684,13 @@ class ChartRecordView(TemplateView):
|
|||||||
media_type = self.request.GET.get("media", "Track")
|
media_type = self.request.GET.get("media", "Track")
|
||||||
user = self.request.user
|
user = self.request.user
|
||||||
params = {}
|
params = {}
|
||||||
|
context_data["chart_type"] = self.request.GET.get(
|
||||||
|
"chart_type", "maloja"
|
||||||
|
)
|
||||||
context_data["artist_charts"] = {}
|
context_data["artist_charts"] = {}
|
||||||
|
|
||||||
if not date:
|
if not date:
|
||||||
limit = 20
|
limit = 20
|
||||||
artist_params = {"user": user, "media_type": "Artist"}
|
|
||||||
context_data["current_artist_charts"] = {
|
|
||||||
"today": live_charts(
|
|
||||||
**artist_params, chart_period="today", limit=limit
|
|
||||||
),
|
|
||||||
"week": live_charts(
|
|
||||||
**artist_params, chart_period="week", limit=limit
|
|
||||||
),
|
|
||||||
"month": live_charts(
|
|
||||||
**artist_params, chart_period="month", limit=limit
|
|
||||||
),
|
|
||||||
"year": live_charts(
|
|
||||||
**artist_params, chart_period="year", limit=limit
|
|
||||||
),
|
|
||||||
"all": live_charts(**artist_params, limit=limit),
|
|
||||||
}
|
|
||||||
|
|
||||||
track_params = {"user": user, "media_type": "Track"}
|
|
||||||
context_data["current_track_charts"] = {
|
|
||||||
"today": live_charts(
|
|
||||||
**track_params, chart_period="today", limit=limit
|
|
||||||
),
|
|
||||||
"week": live_charts(
|
|
||||||
**track_params, chart_period="week", limit=limit
|
|
||||||
),
|
|
||||||
"month": live_charts(
|
|
||||||
**track_params, chart_period="month", limit=limit
|
|
||||||
),
|
|
||||||
"year": live_charts(
|
|
||||||
**track_params, chart_period="year", limit=limit
|
|
||||||
),
|
|
||||||
"all": live_charts(**track_params, limit=limit),
|
|
||||||
}
|
|
||||||
|
|
||||||
limit = 14
|
|
||||||
artist = {"user": user, "media_type": "Artist", "limit": limit}
|
artist = {"user": user, "media_type": "Artist", "limit": limit}
|
||||||
# This is weird. They don't display properly as QuerySets, so we cast to lists
|
# This is weird. They don't display properly as QuerySets, so we cast to lists
|
||||||
context_data["chart_keys"] = {
|
context_data["chart_keys"] = {
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
{% load humanize %}
|
{% load humanize %}
|
||||||
{% load naturalduration %}
|
{% load naturalduration %}
|
||||||
<div>
|
<div>
|
||||||
<h2>Last Scrobbles</h2>
|
|
||||||
<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>
|
<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 class="row">
|
<div class="row">
|
||||||
@ -30,6 +29,22 @@
|
|||||||
<button class="nav-link" id="profile-tab" data-bs-toggle="tab" data-bs-target="#latest-boardgames"
|
<button class="nav-link" id="profile-tab" data-bs-toggle="tab" data-bs-target="#latest-boardgames"
|
||||||
type="button" role="tab" aria-controls="profile" aria-selected="false">Board Games</button>
|
type="button" role="tab" aria-controls="profile" aria-selected="false">Board Games</button>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="nav-item" role="presentation">
|
||||||
|
<button class="nav-link" id="profile-tab" data-bs-toggle="tab" data-bs-target="#latest-webpages"
|
||||||
|
type="button" role="tab" aria-controls="profile" aria-selected="false">Web Pages</button>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item" role="presentation">
|
||||||
|
<button class="nav-link" id="profile-tab" data-bs-toggle="tab" data-bs-target="#latest-beers"
|
||||||
|
type="button" role="tab" aria-controls="profile" aria-selected="false">Beers</button>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item" role="presentation">
|
||||||
|
<button class="nav-link" id="profile-tab" data-bs-toggle="tab" data-bs-target="#latest-books"
|
||||||
|
type="button" role="tab" aria-controls="profile" aria-selected="false">Books</button>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item" role="presentation">
|
||||||
|
<button class="nav-link" id="profile-tab" data-bs-toggle="tab" data-bs-target="#latest-tasks"
|
||||||
|
type="button" role="tab" aria-controls="profile" aria-selected="false">Tasks</button>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<div class="tab-content" id="myTabContent2">
|
<div class="tab-content" id="myTabContent2">
|
||||||
@ -46,9 +61,10 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for scrobble in object_list %}
|
{{Track|length}}
|
||||||
|
{% for scrobble in Track %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{scrobble.timestamp|naturaltime}}</td>
|
<td>{% if scrobble.in_progress %}Listening now{% else %}{{scrobble.timestamp|naturaltime}}{% endif %}</td>
|
||||||
{% if scrobble.track.album.cover_image %}
|
{% 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>
|
<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 %}
|
{% else %}
|
||||||
@ -65,7 +81,6 @@
|
|||||||
|
|
||||||
<div class="tab-pane fade show" id="latest-watched" role="tabpanel"
|
<div class="tab-pane fade show" id="latest-watched" role="tabpanel"
|
||||||
aria-labelledby="latest-watched-tab">
|
aria-labelledby="latest-watched-tab">
|
||||||
<h2>Latest watched</h2>
|
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table class="table table-striped table-sm">
|
<table class="table table-striped table-sm">
|
||||||
<thead>
|
<thead>
|
||||||
@ -77,9 +92,9 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for scrobble in video_scrobble_list %}
|
{% for scrobble in Video %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{scrobble.timestamp|naturaltime}}</td>
|
<td>{% if scrobble.in_progress %}Watching now{% else %}{{scrobble.timestamp|naturaltime}}{% endif %}</td>
|
||||||
{% if scrobble.video.cover_image %}
|
{% 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>
|
<td><img src="{{scrobble.media_obj.cover_image_medium.url}}" width=25 height=25 style="border:1px solid black;" /></td>
|
||||||
{% else %}
|
{% else %}
|
||||||
@ -105,15 +120,17 @@
|
|||||||
<th scope="col">Title</th>
|
<th scope="col">Title</th>
|
||||||
<th scope="col">Round</th>
|
<th scope="col">Round</th>
|
||||||
<th scope="col">League</th>
|
<th scope="col">League</th>
|
||||||
|
<th scope="col">Time watched</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for scrobble in sport_scrobble_list %}
|
{% for scrobble in SportEvent %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{scrobble.timestamp|naturaltime}}</td>
|
<td>{% if scrobble.in_progress %}Watching now{% else %}{{scrobble.timestamp|naturaltime}}{% endif %}</td>
|
||||||
<td>{{scrobble.sport_event.title}}</td>
|
<td>{{scrobble.sport_event.title}}</td>
|
||||||
<td>{{scrobble.sport_event.round.name}}</td>
|
<td>{{scrobble.sport_event.round.name}}</td>
|
||||||
<td>{{scrobble.sport_event.round.season.league}}</td>
|
<td>{{scrobble.sport_event.round.season.league}}</td>
|
||||||
|
<td>{{scrobble.playback_position_seconds|natural_duration}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
@ -123,7 +140,6 @@
|
|||||||
|
|
||||||
<div class="tab-pane fade show" id="latest-podcasted" role="tabpanel"
|
<div class="tab-pane fade show" id="latest-podcasted" role="tabpanel"
|
||||||
aria-labelledby="latest-podcasted-tab">
|
aria-labelledby="latest-podcasted-tab">
|
||||||
<h2>Latest Podcasted</h2>
|
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table class="table table-striped table-sm">
|
<table class="table table-striped table-sm">
|
||||||
<thead>
|
<thead>
|
||||||
@ -134,8 +150,9 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for scrobble in podcast_scrobble_list %}
|
{% for scrobble in PodcastEpisode %}
|
||||||
<tr>
|
<tr>
|
||||||
|
<td>{% if scrobble.in_progress %}Listening now{% else %}{{scrobble.timestamp|naturaltime}}{% endif %}</td>
|
||||||
<td>{{scrobble.timestamp|naturaltime}}</td>
|
<td>{{scrobble.timestamp|naturaltime}}</td>
|
||||||
<td>{{scrobble.podcast_episode.title}}</td>
|
<td>{{scrobble.podcast_episode.title}}</td>
|
||||||
<td>{{scrobble.podcast_episode.podcast}}</td>
|
<td>{{scrobble.podcast_episode.podcast}}</td>
|
||||||
@ -148,7 +165,6 @@
|
|||||||
|
|
||||||
<div class="tab-pane fade show" id="latest-videogames" role="tabpanel"
|
<div class="tab-pane fade show" id="latest-videogames" role="tabpanel"
|
||||||
aria-labelledby="latest-videogames-tab">
|
aria-labelledby="latest-videogames-tab">
|
||||||
<h2>Latest Video Games</h2>
|
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table class="table table-striped table-sm">
|
<table class="table table-striped table-sm">
|
||||||
<thead>
|
<thead>
|
||||||
@ -156,14 +172,14 @@
|
|||||||
<th scope="col">Date</th>
|
<th scope="col">Date</th>
|
||||||
<th scope="col">Cover/Screenshot</th>
|
<th scope="col">Cover/Screenshot</th>
|
||||||
<th scope="col">Title</th>
|
<th scope="col">Title</th>
|
||||||
<th scope="col">Time played (mins)</th>
|
<th scope="col">Time played</th>
|
||||||
<th scope="col">Percent complete</th>
|
<th scope="col">Percent complete</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for scrobble in videogame_scrobble_list %}
|
{% for scrobble in VideoGame %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{scrobble.timestamp|naturaltime}}</td>
|
<td>{% if scrobble.in_progress %}Sessioning now{% else %}{{scrobble.timestamp|naturaltime}}{% endif %}</td>
|
||||||
{% if scrobble.screenshot %}
|
{% if scrobble.screenshot %}
|
||||||
<td><img src="{{scrobble.screenshot_medium.url}}" width=25 height=25 style="border:1px solid black;" /></td>
|
<td><img src="{{scrobble.screenshot_medium.url}}" width=25 height=25 style="border:1px solid black;" /></td>
|
||||||
{% else %}
|
{% else %}
|
||||||
@ -184,7 +200,6 @@
|
|||||||
|
|
||||||
<div class="tab-pane fade show" id="latest-boardgames" role="tabpanel"
|
<div class="tab-pane fade show" id="latest-boardgames" role="tabpanel"
|
||||||
aria-labelledby="latest-boardgames-tab">
|
aria-labelledby="latest-boardgames-tab">
|
||||||
<h2>Latest Board Games</h2>
|
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table class="table table-striped table-sm">
|
<table class="table table-striped table-sm">
|
||||||
<thead>
|
<thead>
|
||||||
@ -192,13 +207,13 @@
|
|||||||
<th scope="col">Date</th>
|
<th scope="col">Date</th>
|
||||||
<th scope="col">Cover</th>
|
<th scope="col">Cover</th>
|
||||||
<th scope="col">Title</th>
|
<th scope="col">Title</th>
|
||||||
<th scope="col">Time played (mins)</th>
|
<th scope="col">Time played</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for scrobble in boardgame_scrobble_list %}
|
{% for scrobble in BoardGame %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{scrobble.timestamp|naturaltime}}</td>
|
<td>{% if scrobble.in_progress %}Tabling now{% 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><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><a href="{{scrobble.media_obj.get_absolute_url}}">{{scrobble.media_obj.title}}</a></td>
|
||||||
<td>{{scrobble.playback_position_seconds|natural_duration}}</td>
|
<td>{{scrobble.playback_position_seconds|natural_duration}}</td>
|
||||||
@ -208,5 +223,108 @@
|
|||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="tab-pane fade show" id="latest-webpages" role="tabpanel"
|
||||||
|
aria-labelledby="latest-webpages-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 browsing</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for scrobble in WebPage %}
|
||||||
|
<tr>
|
||||||
|
<td>{% if scrobble.in_progress %}Browsing now{% 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.playback_position_seconds|natural_duration}}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<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">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 %}Drinking now{% 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.playback_position_seconds|natural_duration}}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="tab-pane fade show" id="latest-books" role="tabpanel"
|
||||||
|
aria-labelledby="latest-books-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 reading</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for scrobble in Book %}
|
||||||
|
<tr>
|
||||||
|
<td>{% if scrobble.in_progress %}Reading now{% 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.playback_position_seconds|natural_duration}}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<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 %}Doing now{% else %}{{scrobble.timestamp|naturaltime}}{% endif %}</td>
|
||||||
|
<td><a href="{{scrobble.media_obj.get_absolute_url}}">{{scrobble.media_obj.title}}</a></td>
|
||||||
|
<td>{{scrobble.playback_position_seconds|natural_duration}}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user