Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6306390f82 | |||
| 350d3ceb14 | |||
| a1ff82bfec | |||
| 92c0c668b3 | |||
| 3b77feda45 | |||
| 45c402f8c1 | |||
| 90a1398438 | |||
| c7a81802ac | |||
| a9a8678ac0 | |||
| cbf0583871 | |||
| 5cac1fe109 | |||
| 6782ed312d | |||
| fda505ea4e | |||
| 8db111f66f | |||
| ee1cae496a | |||
| 9403c68184 | |||
| 96030f4a99 | |||
| a8c3925af4 | |||
| a2f507a976 | |||
| 7a7edc6e47 | |||
| af6c39fb85 | |||
| 36cfdd6f6c | |||
| b11d87af75 | |||
| 1cf50209a4 | |||
| 8a5486fb2c | |||
| 135d6e65fa | |||
| 965f2dd41b |
36
PROJECT.org
36
PROJECT.org
@ -442,6 +442,42 @@ it's annoying.
|
||||
** TODO [#C] Allow users to see tasks on calendar view :vrobbler:personal:project:templates:feature:
|
||||
https://codepen.io/oliviale/pen/QYqybo
|
||||
** TODO [#C] Come up with a possible flow using WebDAV and super-productivity for tasks :personal:feature:project:vrobbler:tasks:
|
||||
* Version 26.0 [3/3]
|
||||
** DONE Clean up templates for scrobble details :vrobbler:personal:bug:templates:
|
||||
:PROPERTIES:
|
||||
:ID: 43dc1e02-c110-5b49-0ac7-4c4f7656d1aa
|
||||
:END:
|
||||
** DONE Add named locations visited to dashboard :vrobbler:personal:feature:locations:templates:
|
||||
:PROPERTIES:
|
||||
:ID: ebc365a1-cef4-f75d-569f-c24b072ef5a4
|
||||
:END:
|
||||
** DONE Add moods to dashboard :vrobbler:moods:feature:templates:personal:
|
||||
:PROPERTIES:
|
||||
:ID: c03a38ce-b337-f4fa-adba-aee08d4329f5
|
||||
:END:
|
||||
* Version 25.0 [3/3]
|
||||
** DONE Add basic food templates and fix urls :food:vrobbler:personal:project:bug:urls:
|
||||
:PROPERTIES:
|
||||
:ID: 3de3459e-8e7e-abba-e068-b919a819d3e3
|
||||
:END:
|
||||
** DONE [#C] Fix how elapsed time is calculated :vrobbler:personal:project:scrobbles:bug:
|
||||
:PROPERTIES:
|
||||
:ID: cff58fc4-06ac-8016-4eae-130b51e3c9b7
|
||||
:END:
|
||||
** DONE Fix templates for videos and dashboard links :personal:feature:project:vrobbler:templates:
|
||||
:PROPERTIES:
|
||||
:ID: 7debfbaf-cdd8-f49b-57ff-804bfe7c9236
|
||||
:END:
|
||||
* Version 24.0 [2/2]
|
||||
** DONE Clean up logdata for various media :personal:feature:project:vrobbler:logdata:
|
||||
:PROPERTIES:
|
||||
:ID: d5cce807-1f45-ef19-45a4-9f7069fa2a93
|
||||
:END:
|
||||
** DONE Removed sidebar and add links to headers :personal:feature:templates:scrobbles:
|
||||
:PROPERTIES:
|
||||
:ID: 1a1c0aa6-0313-c8be-1676-5d6adddef0a4
|
||||
:END:
|
||||
|
||||
* Version 23.0 [3/3]
|
||||
** DONE Add dynamic forms for LogData classes :personal:feature:vrobbler:project:forms:logdata:
|
||||
:PROPERTIES:
|
||||
|
||||
18
vrobbler/apps/foods/migrations/0003_food_calories.py
Normal file
18
vrobbler/apps/foods/migrations/0003_food_calories.py
Normal file
@ -0,0 +1,18 @@
|
||||
# Generated by Django 4.2.19 on 2025-09-11 13:35
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('foods', '0002_alter_food_run_time_seconds'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='food',
|
||||
name='calories',
|
||||
field=models.IntegerField(blank=True, null=True),
|
||||
),
|
||||
]
|
||||
@ -8,14 +8,15 @@ from django.urls import reverse
|
||||
from django_extensions.db.models import TimeStampedModel
|
||||
from imagekit.models import ImageSpecField
|
||||
from imagekit.processors import ResizeToFit
|
||||
from scrobbles.dataclasses import BaseLogData
|
||||
from scrobbles.dataclasses import BaseLogData, WithPeopleLogData
|
||||
from scrobbles.mixins import ScrobblableConstants, ScrobblableMixin
|
||||
|
||||
BNULL = {"blank": True, "null": True}
|
||||
|
||||
|
||||
@dataclass
|
||||
class FoodLogData(BaseLogData):
|
||||
class FoodLogData(BaseLogData, WithPeopleLogData):
|
||||
calories: Optional[int] = None
|
||||
meal: Optional[str] = None
|
||||
rating: Optional[str] = None
|
||||
|
||||
@ -48,6 +49,7 @@ class FoodCategory(TimeStampedModel):
|
||||
|
||||
class Food(ScrobblableMixin):
|
||||
description = models.TextField(**BNULL)
|
||||
calories = models.IntegerField(**BNULL)
|
||||
allrecipe_image = models.ImageField(upload_to="food/recipe/", **BNULL)
|
||||
allrecipe_image_small = ImageSpecField(
|
||||
source="allrecipe_image",
|
||||
@ -72,7 +74,8 @@ class Food(ScrobblableMixin):
|
||||
|
||||
@property
|
||||
def subtitle(self):
|
||||
return self.category.name
|
||||
if self.category:
|
||||
return self.category.name
|
||||
|
||||
@property
|
||||
def strings(self) -> ScrobblableConstants:
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
from dataclasses import dataclass
|
||||
from typing import Optional
|
||||
from django.apps import apps
|
||||
from django.db import models
|
||||
from django.urls import reverse
|
||||
|
||||
@ -1,11 +1,13 @@
|
||||
from decimal import Decimal, getcontext
|
||||
import logging
|
||||
from dataclasses import dataclass
|
||||
from decimal import Decimal
|
||||
from typing import Dict
|
||||
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.conf import settings
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.db import models
|
||||
from django.urls import reverse
|
||||
from scrobbles.dataclasses import BaseLogData, WithPeopleLogData
|
||||
from scrobbles.mixins import ScrobblableConstants, ScrobblableMixin
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@ -15,6 +17,9 @@ User = get_user_model()
|
||||
GEOLOC_ACCURACY = int(getattr(settings, "GEOLOC_ACCURACY", 4))
|
||||
GEOLOC_PROXIMITY = Decimal(getattr(settings, "GEOLOC_PROXIMITY", "0.0001"))
|
||||
|
||||
@dataclass
|
||||
class GeoLocationLogData(BaseLogData, WithPeopleLogData):
|
||||
pass
|
||||
|
||||
class GeoLocation(ScrobblableMixin):
|
||||
COMPLETION_PERCENT = getattr(settings, "LOCATION_COMPLETION_PERCENT", 100)
|
||||
@ -36,9 +41,13 @@ class GeoLocation(ScrobblableMixin):
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse(
|
||||
"locations:geo_location_detail", kwargs={"slug": self.uuid}
|
||||
"locations:geolocation_detail", kwargs={"slug": self.uuid}
|
||||
)
|
||||
|
||||
@property
|
||||
def logdata_cls(self):
|
||||
return GeoLocationLogData
|
||||
|
||||
@classmethod
|
||||
def find_or_create(cls, data_dict: Dict) -> "GeoLocation":
|
||||
"""Given a data dict from GPSLogger, does the heavy lifting of looking up
|
||||
|
||||
@ -8,11 +8,11 @@ urlpatterns = [
|
||||
path(
|
||||
"locations/",
|
||||
views.GeoLocationListView.as_view(),
|
||||
name="geo_locations_list",
|
||||
name="geolocation_list",
|
||||
),
|
||||
path(
|
||||
"locations/<slug:slug>/",
|
||||
views.GeoLocationDetailView.as_view(),
|
||||
name="geo_location_detail",
|
||||
name="geolocation_detail",
|
||||
),
|
||||
]
|
||||
|
||||
@ -42,7 +42,7 @@ class Mood(ScrobblableMixin):
|
||||
return str(self.uuid)
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse("moods:mood-detail", kwargs={"slug": self.uuid})
|
||||
return reverse("moods:mood_detail", kwargs={"slug": self.uuid})
|
||||
|
||||
@property
|
||||
def subtitle(self) -> str:
|
||||
|
||||
@ -5,10 +5,10 @@ app_name = "moods"
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
path("moods/", views.MoodListView.as_view(), name="mood-list"),
|
||||
path("moods/", views.MoodListView.as_view(), name="mood_list"),
|
||||
path(
|
||||
"moods/<slug:slug>/",
|
||||
views.MoodDetailView.as_view(),
|
||||
name="mood-detail",
|
||||
name="mood_detail",
|
||||
),
|
||||
]
|
||||
|
||||
@ -573,7 +573,7 @@ class Album(TimeStampedModel):
|
||||
alt_name = name
|
||||
|
||||
album = Album.objects.filter(
|
||||
name=found_name, musicbrainz_id=album_dict.get("mbid")
|
||||
musicbrainz_id=album_dict.get("mbid")
|
||||
).first()
|
||||
|
||||
if not album:
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
from django.views import generic
|
||||
from podcasts.models import Podcast
|
||||
|
||||
from scrobbles.views import ScrobbleableListView, ScrobbleableDetailView
|
||||
|
||||
class PodcastListView(generic.ListView):
|
||||
model = Podcast
|
||||
|
||||
@ -3,8 +3,6 @@ from typing import Union, get_args, get_origin
|
||||
|
||||
from django import forms
|
||||
|
||||
from people.models import Person
|
||||
|
||||
|
||||
class ExportScrobbleForm(forms.Form):
|
||||
"""Provide options for downloading scrobbles"""
|
||||
@ -76,10 +74,16 @@ def django_form_field_from_type(field_type, required=True):
|
||||
|
||||
def form_from_dataclass(dataclass):
|
||||
form_fields = {}
|
||||
# Override notes field
|
||||
override_fields = {}
|
||||
for klass in dataclass.mro():
|
||||
if hasattr(klass, "override_fields"):
|
||||
print(klass, ": ", klass.override_fields())
|
||||
override_fields.update(klass.override_fields())
|
||||
print("overrides: ", override_fields)
|
||||
for f in fields(dataclass):
|
||||
if f.name in dataclass.override_fields():
|
||||
form_fields[f.name] = dataclass.override_fields()[f.name]
|
||||
print(f)
|
||||
if f.name in override_fields:
|
||||
form_fields[f.name] = override_fields[f.name]
|
||||
continue
|
||||
|
||||
required = f.default is None and f.default_factory is None
|
||||
|
||||
@ -655,6 +655,13 @@ class Scrobble(TimeStampedModel):
|
||||
(scrobble.elapsed_time)
|
||||
)
|
||||
|
||||
# Remove any locations without titles
|
||||
if "GeoLocation" in scrobbles_by_type.keys():
|
||||
for loc_scrobble in scrobbles_by_type["GeoLocation"]:
|
||||
if not loc_scrobble.media_obj.title:
|
||||
scrobbles_by_type["GeoLocation"].remove(loc_scrobble)
|
||||
scrobbles_by_type["GeoLocation_count"] -= 1
|
||||
|
||||
return scrobbles_by_type
|
||||
|
||||
@classmethod
|
||||
@ -892,8 +899,9 @@ class Scrobble(TimeStampedModel):
|
||||
if self.played_to_completion:
|
||||
if self.playback_position_seconds:
|
||||
return self.playback_position_seconds
|
||||
if self.media_obj.run_time_seconds:
|
||||
if self.media_obj and self.media_obj.run_time_seconds:
|
||||
return self.media_obj.run_time_seconds
|
||||
|
||||
return (timezone.now() - self.timestamp).seconds
|
||||
|
||||
@property
|
||||
@ -1070,6 +1078,8 @@ class Scrobble(TimeStampedModel):
|
||||
media_obj = self.puzzle
|
||||
if self.task:
|
||||
media_obj = self.task
|
||||
if self.food:
|
||||
media_obj = self.food
|
||||
return media_obj
|
||||
|
||||
def __str__(self):
|
||||
|
||||
@ -47,8 +47,8 @@ class ScrobbleNtfyNotification(ScrobbleNotification):
|
||||
self.click_url = self.url_tmpl.format(path=self.scrobble.finish_url)
|
||||
self.title = "Finish " + self.media_obj.strings.verb.lower() + "?"
|
||||
|
||||
if self.scrobble.log and isinstance(self.scrobble.log, dict) and self.scrobble.log.get("description"):
|
||||
self.ntfy_str += f" - {self.scrobble.log.get('description')}"
|
||||
if self.scrobble.log and isinstance(self.scrobble.log, dict) and self.scrobble.log.get("title"):
|
||||
self.ntfy_str += f" - {self.scrobble.log.get('title')}"
|
||||
|
||||
def send(self):
|
||||
if (
|
||||
@ -72,7 +72,7 @@ class MoodNtfyNotification(BasicNtfyNotification):
|
||||
def __init__(self, profile, **kwargs):
|
||||
super().__init__(profile)
|
||||
self.ntfy_str: str = "Would you like to check in about your mood?"
|
||||
self.click_url = self.url_tmpl.format(path=reverse("moods:mood-list"))
|
||||
self.click_url = self.url_tmpl.format(path=reverse("moods:mood_list"))
|
||||
self.title = "Mood Check-in!"
|
||||
|
||||
def send(self):
|
||||
|
||||
@ -368,7 +368,7 @@ def email_scrobble_board_game(
|
||||
enriched_game = find_and_enrich_board_game_data(game)
|
||||
if game.get("isBaseGame"):
|
||||
base_games[game.get("id")] = enriched_game
|
||||
elif game.get("isExpansion"):
|
||||
if game.get("isExpansion"):
|
||||
expansions[game.get("id")] = enriched_game
|
||||
|
||||
locations = {}
|
||||
|
||||
@ -2,6 +2,7 @@ import hashlib
|
||||
import logging
|
||||
import re
|
||||
from datetime import datetime, timedelta
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
from urllib.parse import urlparse
|
||||
from zoneinfo import ZoneInfo
|
||||
|
||||
@ -13,7 +14,10 @@ from django.utils import timezone
|
||||
from profiles.models import UserProfile
|
||||
from profiles.utils import now_user_timezone
|
||||
from scrobbles.constants import LONG_PLAY_MEDIA
|
||||
from scrobbles.notifications import MoodNtfyNotification, ScrobbleNtfyNotification
|
||||
from scrobbles.notifications import (
|
||||
MoodNtfyNotification,
|
||||
ScrobbleNtfyNotification,
|
||||
)
|
||||
from scrobbles.tasks import (
|
||||
process_koreader_import,
|
||||
process_lastfm_import,
|
||||
@ -21,6 +25,9 @@ from scrobbles.tasks import (
|
||||
)
|
||||
from webdav.client import get_webdav_client
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from scrobbles.models import Scrobble
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
User = get_user_model()
|
||||
|
||||
@ -345,3 +352,22 @@ def extract_domain(url):
|
||||
+ parsed_url.netloc.split(".")[-1]
|
||||
)
|
||||
return domain
|
||||
|
||||
def fix_playback_position_seconds(*scrobbles: "Scrobble", commit=True) -> list["Scrobble"]:
|
||||
updated_scrobbles = list()
|
||||
for scrobble in scrobbles:
|
||||
if not scrobble.media_obj:
|
||||
logger.info(f"No media object found for scrobble {scrobble.id}, cannot update elapsed time")
|
||||
continue
|
||||
|
||||
if scrobble.media_type == "Track" and scrobble.media_obj.run_time_seconds:
|
||||
too_long = scrobble.playback_position_seconds > scrobble.media_obj.run_time_seconds
|
||||
zero = scrobble.playback_position_seconds == 0
|
||||
null = not scrobble.playback_position_seconds
|
||||
if too_long or zero or null:
|
||||
scrobble.playback_position_seconds = scrobble.media_obj.run_time_seconds
|
||||
updated_scrobbles.append(scrobble)
|
||||
if commit:
|
||||
scrobble.save(update_fields=["playback_position_seconds"])
|
||||
|
||||
return updated_scrobbles
|
||||
|
||||
@ -940,7 +940,7 @@ class ScrobbleDetailView(DetailView):
|
||||
slug_url_kwarg = "uuid"
|
||||
|
||||
def get_form_class(self):
|
||||
return self.object.media_obj.logdata_cls.form()
|
||||
return self.object.media_obj.logdata_cls().form()
|
||||
|
||||
def get_form(self):
|
||||
FormClass = self.get_form_class()
|
||||
@ -969,11 +969,14 @@ class ScrobbleDetailView(DetailView):
|
||||
original_value = (self.object.log or {}).get(field_name)
|
||||
data[field_name] = original_value
|
||||
|
||||
if "with_people_ids" in data:
|
||||
if data.get("with_people_ids", False):
|
||||
data["with_people_ids"] = [
|
||||
p.id for p in data["with_people_ids"]
|
||||
]
|
||||
|
||||
if data.get("platform_id", False):
|
||||
data["platform_id"] = data["platform_id"].id
|
||||
|
||||
self.object.log = data
|
||||
self.object.save(update_fields=["log"])
|
||||
return redirect(self.object.get_absolute_url())
|
||||
|
||||
@ -82,7 +82,7 @@ class Task(LongPlayScrobblableMixin):
|
||||
|
||||
def subtitle_for_user(self, user_id):
|
||||
scrobble = self.scrobbles(user_id).first()
|
||||
return scrobble.logdata.description or ""
|
||||
return scrobble.logdata.title or ""
|
||||
|
||||
@classmethod
|
||||
def find_or_create(cls, title: str) -> "Task":
|
||||
|
||||
@ -39,20 +39,13 @@ class VideoGameLogData(BaseLogData, LongPlayLogData, WithPeopleLogData):
|
||||
|
||||
@classmethod
|
||||
def override_fields(cls) -> dict:
|
||||
fields = {}
|
||||
for base in cls.mro()[1:]:
|
||||
if hasattr(base, "override_fields"):
|
||||
base_fields = base.override_fields()
|
||||
fields.update(base_fields)
|
||||
custom_fields = {
|
||||
return {
|
||||
"platform_id": forms.ModelChoiceField(
|
||||
queryset=VideoGamePlatform.objects.all(),
|
||||
required=False,
|
||||
widget=forms.Select(),
|
||||
)
|
||||
}
|
||||
fields.update(custom_fields)
|
||||
return fields
|
||||
|
||||
|
||||
class VideoGamePlatform(TimeStampedModel):
|
||||
@ -70,19 +63,6 @@ class VideoGamePlatform(TimeStampedModel):
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class VideoGameLogData(BaseLogData, LongPlayLogData, WithPeopleLogData):
|
||||
platform_id: Optional[int] = None
|
||||
emulated: Optional[bool] = False
|
||||
emulator: Optional[str] = None
|
||||
|
||||
@property
|
||||
def platform(self) -> VideoGamePlatform | None:
|
||||
if not self.platform_id:
|
||||
return
|
||||
return VideoGamePlatform.objects.filter(id=self.platform_id).first()
|
||||
|
||||
|
||||
class VideoGameCollection(TimeStampedModel):
|
||||
name = models.CharField(max_length=255)
|
||||
uuid = models.UUIDField(default=uuid4, editable=False, **BNULL)
|
||||
|
||||
@ -5,7 +5,6 @@ app_name = "videos"
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
# path('', views.scrobble_endpoint, name='scrobble-list'),
|
||||
path("movies/", views.MovieListView.as_view(), name="movie_list"),
|
||||
path("series/", views.SeriesListView.as_view(), name="series_list"),
|
||||
path(
|
||||
@ -13,6 +12,7 @@ urlpatterns = [
|
||||
views.SeriesDetailView.as_view(),
|
||||
name="series_detail",
|
||||
),
|
||||
path('videos/', views.VideoListView.as_view(), name='video_list'),
|
||||
path(
|
||||
"video/<slug:slug>/",
|
||||
views.VideoDetailView.as_view(),
|
||||
|
||||
@ -67,10 +67,10 @@ class WebPage(ScrobblableMixin):
|
||||
self.save(update_fields=["extract"])
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse("webpages:webpage-detail", kwargs={"slug": self.uuid})
|
||||
return reverse("webpages:webpage_detail", kwargs={"slug": self.uuid})
|
||||
|
||||
def get_read_url(self):
|
||||
return reverse("webpages:webpage-read", kwargs={"slug": self.uuid})
|
||||
return reverse("webpages:webpage_read", kwargs={"slug": self.uuid})
|
||||
|
||||
@property
|
||||
def estimated_time_to_read_in_seconds(self):
|
||||
|
||||
@ -5,15 +5,15 @@ app_name = "webpages"
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
path("webpages/", views.WebPageListView.as_view(), name="webpage-list"),
|
||||
path("webpages/", views.WebPageListView.as_view(), name="webpage_list"),
|
||||
path(
|
||||
"webpages/<slug:slug>/",
|
||||
views.WebPageDetailView.as_view(),
|
||||
name="webpage-detail",
|
||||
name="webpage_detail",
|
||||
),
|
||||
path(
|
||||
"webpage/<slug:slug>/read/",
|
||||
views.WebPageReadView.as_view(),
|
||||
name="webpage-read",
|
||||
name="webpage_read",
|
||||
),
|
||||
]
|
||||
|
||||
@ -32,7 +32,7 @@ class WebPageReadView(
|
||||
if latest_scrobble.played_to_completion:
|
||||
redirect(
|
||||
reverse(
|
||||
"webpages:webpage-detail", kwargs={"slug": webpage.uuid}
|
||||
"webpages:webpage_detail", kwargs={"slug": webpage.uuid}
|
||||
)
|
||||
)
|
||||
return super().get(*args, **kwargs)
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
{% load urlreplace %}
|
||||
{% load naturalduration %}
|
||||
|
||||
<table class="table table-striped table-sm">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Latest</th>
|
||||
<th scope="col">Title</th>
|
||||
<th scope="col">Scrobbles</th>
|
||||
<th scope="col">Start</th>
|
||||
@ -11,6 +13,7 @@
|
||||
<tbody>
|
||||
{% for obj in object_list %}
|
||||
<tr>
|
||||
<td><a href="{{obj.scrobble_set.last.get_absolute_url}}">{{obj.scrobble_set.last.local_timestamp}}
|
||||
<td><a href="{{obj.get_absolute_url}}">{{obj}}</a></td>
|
||||
{% if request.user.is_authenticated %}
|
||||
<td>{{obj.scrobble_count}}</td>
|
||||
|
||||
@ -175,7 +175,7 @@
|
||||
</head>
|
||||
<body>
|
||||
<header class="navbar navbar-dark sticky-top bg-dark flex-md-nowrap p-0 shadow">
|
||||
<a class="navbar-brand col-md-3 col-lg-2 me-0 px-3" href="#">Vrobbler</a>
|
||||
<a class="navbar-brand col-md-3 col-lg-2 me-0 px-3" href="/">Vrobbler</a>
|
||||
<button class="navbar-toggler position-absolute d-md-none collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#sidebarMenu" aria-controls="sidebarMenu" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
@ -208,85 +208,8 @@
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
<ul class="nav flex-column">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" aria-current="page" href="/">
|
||||
<span data-feather="music"></span>
|
||||
Dashboard
|
||||
</a>
|
||||
</li>
|
||||
{% if user.is_authenticated %}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" aria-current="page" href="/charts/">
|
||||
<span data-feather="bar-chart"></span>
|
||||
Charts
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" aria-current="page" href="/locations/">
|
||||
<span data-feather="map"></span>
|
||||
Locations
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/long-plays/">
|
||||
<span data-feather="playv"></span>
|
||||
Long plays
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/moods/">
|
||||
<span data-feather="smiley"></span>
|
||||
Moods
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/books/">
|
||||
<span data-feather="book"></span>
|
||||
Books
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/video-games/">
|
||||
<span data-feather="video"></span>
|
||||
Video games
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/board-games/">
|
||||
<span data-feather="board"></span>
|
||||
Board games
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/trails/">
|
||||
<span data-feather="trail"></span>
|
||||
Trails
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/beers/">
|
||||
<span data-feather="beer"></span>
|
||||
Beers
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/imports/">
|
||||
<span data-feather="log"></span>
|
||||
Imports
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/admin/">
|
||||
<span data-feather="key"></span>
|
||||
Admin
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
{% block extra_nav %}
|
||||
{% endblock %}
|
||||
<hr/>
|
||||
|
||||
{% if now_playing_list and user.is_authenticated %}
|
||||
<ul>
|
||||
@ -296,7 +219,7 @@
|
||||
{% if scrobble.media_obj.primary_image_url %}<div style="float:left;padding-right:10px;padding-bottom:10px;"><img src="{{scrobble.media_obj.primary_image_url}}" /></div>{% endif %}
|
||||
<p><a href="{{scrobble.media_obj.get_absolute_url}}">{{scrobble.media_obj.title}}</a></p>
|
||||
{% if scrobble.media_obj.subtitle %}<p><em><a href="{{scrobble.media_obj.subtitle.get_absolute_url}}">{{scrobble.media_obj.subtitle}}</a></em></p>{% endif %}
|
||||
{% if scrobble.logdata %}{% if scrobble.logdata.description %}<p><em>{{scrobble.logdata.description}}</em></p>{% endif %}{% endif %}
|
||||
{% if scrobble.logdata %}{% if scrobble.logdata.title%}<p><em>{{scrobble.logdata.title}}</em></p>{% endif %}{% endif %}
|
||||
<p><small>{{scrobble.local_timestamp|naturaltime}} from {{scrobble.source}}</small></p>
|
||||
<div class="progress-bar" style="margin-right:5px;">
|
||||
<span class="progress-bar-fill" style="width: {{scrobble.percent_played}}%;"></span>
|
||||
|
||||
@ -55,7 +55,7 @@
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Date</th>
|
||||
<th scope="col">Publisher</th>
|
||||
<th scope="col">Location</th>
|
||||
<th scope="col">Players</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@ -63,7 +63,7 @@
|
||||
{% for scrobble in scrobbles.all|dictsortreversed:"timestamp" %}
|
||||
<tr>
|
||||
<td><a href={{scrobble.get_absolute_url}}>{{scrobble.local_timestamp}}</a></td>
|
||||
<td>{{scrobble.media_obj.publisher}}</td>
|
||||
<td>{{scrobble.logdata.location }}</td>
|
||||
<td>{% if scrobble.logdata.player_log %}{{scrobble.logdata.player_log}}{% else %}No data{% endif %}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
||||
@ -45,7 +45,7 @@
|
||||
{% for scrobble in scrobbles.all|dictsortreversed:"timestamp" %}
|
||||
<tr>
|
||||
<td><a href="{{scrobble.get_absolute_url}}">{{scrobble.local_timestamp}}</a></td>
|
||||
<td>{% if scrobble.long_play_complete == True %}Yes{% endif %}</td>
|
||||
<td>{% if scrobble.logdata.long_play_complete == True %}Yes{% endif %}</td>
|
||||
<td>{% if scrobble.in_progress %}Now reading{% else %}{{scrobble.session_pages_read}}{% endif %}</td>
|
||||
<td>{% for author in scrobble.book.authors.all %}<a href="{{author.get_absolute_url}}">{{author}}</a>{% if not forloop.last %}, {% endif %}{% endfor %}</td>
|
||||
</tr>
|
||||
|
||||
56
vrobbler/templates/bricksets/brickset_detail.html
Normal file
56
vrobbler/templates/bricksets/brickset_detail.html
Normal file
@ -0,0 +1,56 @@
|
||||
{% extends "base_list.html" %}
|
||||
{% load mathfilters %}
|
||||
{% load static %}
|
||||
{% load naturalduration %}
|
||||
|
||||
{% block title %}{{object.title}}{% endblock %}
|
||||
|
||||
{% block lists %}
|
||||
|
||||
<div class="row">
|
||||
|
||||
{% if object.cover%}
|
||||
<p style="float:left; width:402px; padding:0; border: 1px solid #ccc">
|
||||
<img src="{{object.cover.url}}" width=400 />
|
||||
</p>
|
||||
{% endif %}
|
||||
<div style="float:left; width:600px; margin-left:10px; ">
|
||||
{% if object.summary %}
|
||||
<p>{{object.summary|safe|linebreaks|truncatewords:160}}</p>
|
||||
<hr />
|
||||
{% endif %}
|
||||
<p style="float:right;">
|
||||
<a href="{{object.openlibrary_link}}"><img src="{% static " images/openlibrary-logo.png" %}" width=35></a>
|
||||
<a href="{{object.amazon_link}}"><img src="{% static " images/amazon-logo.png" %}" width=35></a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<p>{{scrobbles.count}} scrobbles</p>
|
||||
</div>
|
||||
<div class="row">
|
||||
<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">Completed</th>
|
||||
<th scope="col">Time</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for scrobble in scrobbles.all|dictsortreversed:"timestamp" %}
|
||||
<tr>
|
||||
<td><a href="{{scrobble.get_absolute_url}}">{{scrobble.local_timestamp}}</a></td>
|
||||
<td>{% if scrobble.logdata.long_play_complete == True %}Yes{% endif %}</td>
|
||||
<td>{% if scrobble.in_progress %}Now reading{% else %}{{scrobble.session_pages_read}}{% endif %}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
23
vrobbler/templates/bricksets/brickset_list.html
Normal file
23
vrobbler/templates/bricksets/brickset_list.html
Normal file
@ -0,0 +1,23 @@
|
||||
{% extends "base_list.html" %}
|
||||
|
||||
{% block title %}Brick Sets{% endblock %}
|
||||
|
||||
{% block head_extra %}
|
||||
<style>
|
||||
dl { width: 210px; float:left; margin-right: 10px; }
|
||||
dt a { color:white; text-decoration: none; font-size:smaller; }
|
||||
img { height:200px; width: 200px; object-fit: cover; }
|
||||
dd .right { float:right; }
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block lists %}
|
||||
<div class="row">
|
||||
|
||||
<div class="col-md">
|
||||
<div class="table-responsive">
|
||||
{% include "_scrobblable_list.html" %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
37
vrobbler/templates/foods/food_detail.html
Normal file
37
vrobbler/templates/foods/food_detail.html
Normal file
@ -0,0 +1,37 @@
|
||||
{% extends "base_list.html" %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}{{object.title}}{% endblock %}
|
||||
|
||||
{% block lists %}
|
||||
<div class="row webpage">
|
||||
<div class="webpage-metadata">
|
||||
<p>{{object.description}}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<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">Calories</th>
|
||||
<th scope="col">Notes</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for scrobble in scrobbles.all %}
|
||||
<tr>
|
||||
<td><a href={{scrobble.get_absolute_url}}>{{scrobble.local_timestamp}}</a></td>
|
||||
<td>{% if scrobble.logdata.calories %}{{scrobble.logdata.calories}}{% else %}{{scrobble.media_obj.calories}}{% endif %}</td>
|
||||
<td>{% for note in scrobble.logdata.notes %}{{note}}{% if not forloop.last %}; {% endif%}{% endfor %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
23
vrobbler/templates/foods/food_list.html
Normal file
23
vrobbler/templates/foods/food_list.html
Normal file
@ -0,0 +1,23 @@
|
||||
{% extends "base_list.html" %}
|
||||
|
||||
{% block title %}Foods{% endblock %}
|
||||
|
||||
{% block head_extra %}
|
||||
<style>
|
||||
dl { width: 210px; float:left; margin-right: 10px; }
|
||||
dt a { color:white; text-decoration: none; font-size:smaller; }
|
||||
img { height:200px; width: 200px; object-fit: cover; }
|
||||
dd .right { float:right; }
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block lists %}
|
||||
<div class="row">
|
||||
|
||||
<div class="col-md">
|
||||
<div class="table-responsive">
|
||||
{% include "_scrobblable_list.html" %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@ -57,12 +57,16 @@
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Date</th>
|
||||
<th scope="col">With</th>
|
||||
<th scope="col">Notes</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for scrobble in scrobbles.all|dictsortreversed:"timestamp" %}
|
||||
{% for scrobble in object.scrobble_set.all|dictsortreversed:"timestamp" %}
|
||||
<tr>
|
||||
<td>{{scrobble.local_timestamp|naturaltime}}</td>
|
||||
<td><a href={{scrobble.get_absolute_url}}>{{scrobble.local_timestamp}}</a></td>
|
||||
<td>{% for person in scrobble.logdata.with_people%}{{person}}{% if not forloop.last %}, {% endif%}{% endfor %}</td>
|
||||
<td>{% for note in scrobble.logdata.notes %}{{note}}{% if not forloop.last %}; {% endif%}{% endfor %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
|
||||
@ -67,13 +67,7 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for location in object_list %}
|
||||
<tr>
|
||||
<td>{{location.scrobble_set.count}}</td>
|
||||
<td>{{location.title}}</td>
|
||||
<td><a href="{{location.get_absolute_url}}">{{location.lat}}x{{location.lon}}</a></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% include "_scrobblable_list.html" %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
@ -28,7 +28,7 @@
|
||||
<tbody>
|
||||
{% for scrobble in scrobbles.all %}
|
||||
<tr>
|
||||
<td>{{scrobble.local_timestamp}}</td>
|
||||
<td><a href={{scrobble.get_absolute_url}}>{{scrobble.local_timestamp}}</a></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
|
||||
@ -82,7 +82,7 @@
|
||||
<tbody>
|
||||
{% for scrobble in object.scrobbles %}
|
||||
<tr>
|
||||
<td>{{scrobble.local_timestamp}}</td>
|
||||
<td><a href={{scrobble.get_absolute_url}}>{{scrobble.local_timestamp}}</a></td>
|
||||
<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}}</a></td>
|
||||
</tr>
|
||||
|
||||
@ -83,7 +83,7 @@
|
||||
<tbody>
|
||||
{% for scrobble in object.scrobbles %}
|
||||
<tr>
|
||||
<td>{{scrobble.local_timestamp}}</td>
|
||||
<td><a href={{scrobble.get_absolute_url}}>{{scrobble.local_timestamp}}</a></td>
|
||||
<td><a href="{{scrobble.track.get_absolute_url}}">{{scrobble.track.title}}</a></td>
|
||||
<td><a href="{{scrobble.track.primary_album.get_absolute_url}}">{{scrobble.track.primary_album.name}}</a></td>
|
||||
</tr>
|
||||
|
||||
@ -28,7 +28,7 @@
|
||||
<tbody>
|
||||
{% for scrobble in scrobbles.all %}
|
||||
<tr>
|
||||
<td>{{scrobble.local_timestamp}}</td>
|
||||
<td><a href={{scrobble.get_absolute_url}}>{{scrobble.local_timestamp}}</a></td>
|
||||
<td><a href="{{scrobble.track.get_absolute_url}}">{{scrobble.track.title}}</a></td>
|
||||
<td><a href="{{scrobble.track.primary_album.get_absolute_url}}">{{scrobble.track.primary_album}}</a></td>
|
||||
<td><a href="{{scrobble.track.artist.get_absolute_url}}">{{scrobble.track.artist}}</a></td>
|
||||
|
||||
@ -20,40 +20,7 @@
|
||||
<hr />
|
||||
|
||||
<div class="col-md">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-sm">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Scrobbles</th>
|
||||
<th scope="col">Track</th>
|
||||
<th scope="col">Artist</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for track in object_list %}
|
||||
<tr>
|
||||
<td>{{track.scrobble_set.count}}</td>
|
||||
<td><a href="{{track.get_absolute_url}}">{{track}}</a></td>
|
||||
<td><a href="{{track.artist.get_absolute_url}}">{{track.artist}}</a></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pagination" style="margin-bottom:50px;">
|
||||
<span class="page-links">
|
||||
{% if page_obj.has_previous %}
|
||||
<a href="?page={{ page_obj.previous_page_number }}">previous</a>
|
||||
{% endif %}
|
||||
<span class="page-current">
|
||||
Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}
|
||||
</span>
|
||||
{% if page_obj.has_next %}
|
||||
<a href="?page={{ page_obj.next_page_number }}">next</a>
|
||||
{% endif %}
|
||||
</span>
|
||||
<div class="table-responsive">{% include "_scrobblable_list.html" %}</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
@ -45,7 +45,7 @@
|
||||
<tbody>
|
||||
{% for scrobble in scrobbles.all %}
|
||||
<tr>
|
||||
<td>{{scrobble.local_timestamp}}</td>
|
||||
<td><a href={{scrobble.get_absolute_url}}>{{scrobble.local_timestamp}}</a></td>
|
||||
<td>{{scrobble.podcast_episode}}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
{% extends "base_list.html" %}
|
||||
|
||||
{% block title %}Podcasts{% endblock %}
|
||||
{% block lists %}
|
||||
<div class="row">
|
||||
<div class="col-md">
|
||||
@ -7,20 +8,19 @@
|
||||
<table class="table table-striped table-sm">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Series</th>
|
||||
<th scope="col">Episode</th>
|
||||
<th scope="col">Podcast</th>
|
||||
<th scope="col">Scrobbles</th>
|
||||
<th scope="col">All time</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for obj in object_list %}
|
||||
{% for episode in obj.episode_set.all %}
|
||||
{% for obj in object_list.all %}
|
||||
{{obj.episodes}}
|
||||
{% for episode in obj.podcastepisode_set.all %}
|
||||
<tr>
|
||||
<td><a href="{{episode.get_absolute_url}}">{{episode}}</a></td>
|
||||
<td><a href="{{obj.get_absolute_url}}">{{obj}}</a></td>
|
||||
<td>{{episode.scrobble_set.count}}</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
|
||||
@ -57,7 +57,7 @@
|
||||
<tbody>
|
||||
{% for scrobble in scrobbles.all|dictsortreversed:"timestamp" %}
|
||||
<tr>
|
||||
<td>{{scrobble.local_timestamp}}</td>
|
||||
<td><a href={{scrobble.get_absolute_url}}>{{scrobble.local_timestamp}}</a></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
|
||||
@ -7,90 +7,141 @@
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md">
|
||||
<h3><a href="{% url 'music:tracks_list' %}">Tracks</a></h3>
|
||||
{% if Track %}
|
||||
<h2>Music</h2>
|
||||
{% with scrobbles=Track count=Track_count time=Track_time %}
|
||||
{% include "scrobbles/_scrobble_table.html" %}
|
||||
{% endwith %}
|
||||
{% else %}
|
||||
<p>No tracks today</p>
|
||||
{% endif %}
|
||||
|
||||
<h3><a href="{% url 'foods:food_list' %}">Food</a></h3>
|
||||
{% if Food %}
|
||||
{% with scrobbles=Food count=Food_count time=Food_time %}
|
||||
{% include "scrobbles/_scrobble_table.html" %}
|
||||
{% endwith %}
|
||||
{% else %}
|
||||
<p>No food today</p>
|
||||
{% endif %}
|
||||
|
||||
<h3><a href="{% url 'moods:mood_list' %}">Moods</a></h3>
|
||||
{% if Mood %}
|
||||
{% with scrobbles=Mood count=Mood_count time=Mood_time %}
|
||||
{% include "scrobbles/_scrobble_table.html" %}
|
||||
{% endwith %}
|
||||
{% else %}
|
||||
<p>No moods felt today </p>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
<div class="col-md">
|
||||
|
||||
<h3><a href="{% url 'tasks:task_list' %}">Tasks</a></h3>
|
||||
{% if Task %}
|
||||
<h2>Latest tasks</h2>
|
||||
{% with scrobbles=Task count=Task_count time=Task_time %}
|
||||
{% include "scrobbles/_scrobble_table.html" %}
|
||||
{% endwith %}
|
||||
{% else %}
|
||||
<p>No tasks today</p>
|
||||
{% endif %}
|
||||
|
||||
<h3><a href="{% url 'videos:video_list' %}">Videos</a></h3>
|
||||
{% if Video %}
|
||||
<h2>Videos</h2>
|
||||
{% with scrobbles=Video count=Video_count time=Video_time %}
|
||||
{% include "scrobbles/_scrobble_table.html" %}
|
||||
{% endwith %}
|
||||
{% else %}
|
||||
<p>No videos today</p>
|
||||
{% endif %}
|
||||
|
||||
<h3><a href="{% url 'webpages:webpage_list' %}">Web pages</a></h3>
|
||||
{% if WebPage %}
|
||||
<h4>Web pages</h4>
|
||||
{% with scrobbles=WebPage count=WebPage_count time=WebPage_time %}
|
||||
{% include "scrobbles/_scrobble_table.html" %}
|
||||
{% endwith %}
|
||||
{% else%}
|
||||
<p>No web page read today</p>
|
||||
{% endif %}
|
||||
|
||||
<h3><a href="{% url 'sports:event_list' %}">Sport events</a></h3>
|
||||
{% if SportEvent %}
|
||||
<h2>Sports</h2>
|
||||
{% with scrobbles=SportEvent count=SportEvent_count time=SportEvent_time %}
|
||||
{% include "scrobbles/_scrobble_table.html" %}
|
||||
{% endwith %}
|
||||
{% else %}
|
||||
<p>No sports today</p>
|
||||
{% endif %}
|
||||
|
||||
<h3><a href="{% url 'podcasts:podcast_list' %}">Podcasts</a></h3>
|
||||
{% if PodcastEpisode %}
|
||||
<h2>Latest podcasts</h2>
|
||||
{% with scrobbles=PodcastEpisode count=PodcastEpisode_count time=PodcastEpisode_time %}
|
||||
{% include "scrobbles/_scrobble_table.html" %}
|
||||
{% endwith %}
|
||||
{% else %}
|
||||
<p>No podcasts today</p>
|
||||
{% endif %}
|
||||
|
||||
<h3><a href="{% url "videogames:videogame_list" %}">Video games</a></h3>
|
||||
{% if VideoGame %}
|
||||
<h4>Video games</h4>
|
||||
{% with scrobbles=VideoGame count=VideoGame_count time=VideoGame_time %}
|
||||
{% include "scrobbles/_scrobble_table.html" %}
|
||||
{% endwith %}
|
||||
{% else %}
|
||||
<p>No video games today</p>
|
||||
{% endif %}
|
||||
|
||||
<h3><a href="{% url "boardgames:boardgame_list" %}">Board games</a></h3>
|
||||
{% if BoardGame %}
|
||||
<h4>Board games</h4>
|
||||
{% with scrobbles=BoardGame count=BoardGame_count time=BoardGame_time %}
|
||||
{% include "scrobbles/_scrobble_table.html" %}
|
||||
{% endwith %}
|
||||
{% else %}
|
||||
<p>No board games today</p>
|
||||
{% endif %}
|
||||
|
||||
<h3><a href="{% url 'beers:beer_list' %}">Beers</a></h3>
|
||||
{% if Beer %}
|
||||
<h4>Beers</h4>
|
||||
{% with scrobbles=Beer count=Beer_count time=Beer_time %}
|
||||
{% include "scrobbles/_scrobble_table.html" %}
|
||||
{% endwith %}
|
||||
<p>No beer today</p>
|
||||
{% endif %}
|
||||
|
||||
<h3><a href="{% url 'bricksets:brickset_list' %}">Brick sets</a></h3>
|
||||
{% if BrickSet %}
|
||||
<h4>Brick sets</h4>
|
||||
{% with scrobbles=BrickSet count=BrickSet_count time=BrickSet_time %}
|
||||
{% include "scrobbles/_scrobble_table.html" %}
|
||||
{% endwith %}
|
||||
{% else %}
|
||||
<p>No brick sets today</p>
|
||||
{% endif %}
|
||||
|
||||
<h3><a href="{% url 'puzzles:puzzle_list' %}">Puzzles</a></h3>
|
||||
{% if Puzzle %}
|
||||
<h4>Puzzles</h4>
|
||||
{% with scrobbles=Puzzle count=Puzzle_count time=Puzzle_time %}
|
||||
{% include "scrobbles/_scrobble_table.html" %}
|
||||
{% endwith %}
|
||||
{% else %}
|
||||
<p>No puzzles today</p>
|
||||
{% endif %}
|
||||
|
||||
<h3><a href="{% url 'books:book_list' %}">Books</a></h3>
|
||||
{% if Book %}
|
||||
<h4>Books</h4>
|
||||
{% with scrobbles=Book count=Book_count time=Book_time %}
|
||||
{% include "scrobbles/_scrobble_table.html" %}
|
||||
{% endwith %}
|
||||
{% else %}
|
||||
<p>No books read today</p>
|
||||
{% endif %}
|
||||
|
||||
<h3><a href="{% url 'locations:geolocation_list' %}">Locations</a></h3>
|
||||
{% if GeoLocation %}
|
||||
{% with scrobbles=GeoLocation count=GeoLocation_count time=GeoLocation_time %}
|
||||
{% include "scrobbles/_scrobble_table.html" %}
|
||||
{% endwith %}
|
||||
{% else %}
|
||||
<p>No locations visited today</p>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
{% load humanize %}
|
||||
{% load naturalduration %}
|
||||
<tr {% if scrobble.in_progress %}class="in-progress"{% endif %}>
|
||||
<td>{% if scrobble.in_progress %}{{scrobble.media_obj.strings.verb}} now | <a class="right" href="{% url "scrobbles:finish" scrobble.uuid %}">Finish</a>{% else %}{{scrobble.local_timestamp|naturaltime}}{% endif %}</td>
|
||||
<td>{% if scrobble.in_progress %}<a href="{{scrobble.get_absolute_url}}">{{scrobble.media_obj.strings.verb}} now</a> | <a class="right" href="{% url "scrobbles:finish" scrobble.uuid %}">Finish</a>{% else %}<a href="{{scrobble.get_absolute_url}}">{{scrobble.local_timestamp|naturaltime}}</a>{% endif %}</td>
|
||||
<td>
|
||||
{% if scrobble.media_type == "Task" %}
|
||||
<p><em><a href="{{scrobble.media_obj.get_absolute_url}}">{{scrobble.media_obj.title|truncatechars_html:45}} - {% if scrobble.logdata %}{% if scrobble.logdata.description %}{{scrobble.logdata.description}}{% endif %}{% endif %}</a></em></p>
|
||||
<p><em><a href="{{scrobble.media_obj.get_absolute_url}}">{{scrobble.media_obj.title|truncatechars_html:45}} - {% if scrobble.logdata %}{% if scrobble.logdata.title%}{{scrobble.logdata.title}}{% endif %}{% endif %}</a></em></p>
|
||||
{% else %}
|
||||
<a href="{{scrobble.media_obj.get_absolute_url}}">{{scrobble.media_obj|truncatechars_html:45}}</a>
|
||||
{% endif %}
|
||||
|
||||
@ -12,7 +12,9 @@
|
||||
<h1>{{ object.media_obj }} - {{object.media_type}}</h1>
|
||||
|
||||
<!-- Your existing detail page content -->
|
||||
{% if object.logdata.avg_seconds_per_page %}
|
||||
<p>Rate: {{object.logdata.avg_seconds_per_page}}s per page</p>
|
||||
{% endif %}
|
||||
|
||||
<h2>Edit Log</h2>
|
||||
<form method="post" class="needs-validation" novalidate>
|
||||
|
||||
@ -56,6 +56,14 @@
|
||||
<h1 class="h2">{% if date %}{{date|naturaltime}}{% else %}{{title}}{% endif %}</h1>
|
||||
<div class="btn-toolbar mb-2 mb-md-0">
|
||||
{% if user.is_authenticated %}
|
||||
<div class="btn-group me-2">
|
||||
<form action="/admin/" method="get">
|
||||
<button type="submit" class="btn btn-sm btn-outline-secondary">
|
||||
<span data-feather="key"></span>
|
||||
Admin
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
<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">
|
||||
|
||||
@ -20,7 +20,7 @@
|
||||
<tbody>
|
||||
{% for scrobble in scrobbles.all %}
|
||||
<tr>
|
||||
<td>{{scrobble.local_timestamp}}</td>
|
||||
<td><a href={{scrobble.get_absolute_url}}>{{scrobble.local_timestamp}}</a></td>
|
||||
<td>{{scrobble.media_obj.round.season.name}}</td>
|
||||
<td>{{scrobble.media_obj.round.season.league}}</td>
|
||||
</tr>
|
||||
|
||||
@ -54,7 +54,7 @@
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Date</th>
|
||||
<th scope="col">Description</th>
|
||||
<th scope="col">Title</th>
|
||||
<th scope="col">Notes</th>
|
||||
<th scope="col">Source</th>
|
||||
</tr>
|
||||
@ -63,7 +63,7 @@
|
||||
{% for scrobble in scrobbles.all|dictsortreversed:"timestamp" %}
|
||||
<tr>
|
||||
<td><a href="{{scrobble.get_absolute_url}}">{{scrobble.local_timestamp}}</a></td>
|
||||
<td><a href="{{scrobble.get_media_source_url}}">{{scrobble.logdata.description}}</a></td>
|
||||
<td><a href="{{scrobble.get_media_source_url}}">{{scrobble.logdata.title}}</a></td>
|
||||
<td>{{scrobble.logdata.notes_as_str|safe}}</td>
|
||||
<td>{{scrobble.source}}</td>
|
||||
</tr>
|
||||
|
||||
@ -57,7 +57,7 @@
|
||||
<tbody>
|
||||
{% for scrobble in object.scrobble_set.all|dictsortreversed:"timestamp" %}
|
||||
<tr>
|
||||
<td>{{scrobble.local_timestamp}}</td>
|
||||
<td><a href={{scrobble.get_absolute_url}}>{{scrobble.local_timestamp}}</a></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
|
||||
@ -1,28 +1,32 @@
|
||||
{% extends "base_list.html" %}
|
||||
{% block title %}Movies{% endblock %}
|
||||
{% block head_extra %}
|
||||
<style>
|
||||
dl {
|
||||
width: 210px;
|
||||
float: left;
|
||||
margin-right: 10px;
|
||||
}
|
||||
dt a {
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
font-size: smaller;
|
||||
}
|
||||
img {
|
||||
height: 200px;
|
||||
width: 200px;
|
||||
object-fit: cover;
|
||||
}
|
||||
dd .right {
|
||||
float: right;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block lists %}
|
||||
<div class="row">
|
||||
<div class="col-md">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-sm">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Title</th>
|
||||
<th scope="col">Scrobbles</th>
|
||||
<th scope="col">All time</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for obj in object_list %}
|
||||
<tr>
|
||||
<td><a href="{{obj.get_absolute_url}}">{{obj}}</a></td>
|
||||
<td>{{obj.scrobble_set.count}}</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md">
|
||||
<div class="table-responsive">{% include "_scrobblable_list.html" %}</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
||||
@ -60,7 +60,7 @@
|
||||
<tbody>
|
||||
{% for scrobble in scrobbles %}
|
||||
<tr>
|
||||
<td>{{scrobble.local_timestamp}}</td>
|
||||
<td><a href={{scrobble.get_absolute_url}}>{{scrobble.local_timestamp}}</a></td>
|
||||
<td><a href="{{scrobble.media_obj.get_absolute_url}}">{{scrobble.media_obj.title}}</a></td>
|
||||
<td>{{scrobble.media_obj.season_number}}</td>
|
||||
<td>{{scrobble.media_obj.episode_number}}</td>
|
||||
|
||||
@ -1,32 +1,32 @@
|
||||
{% extends "base_list.html" %}
|
||||
{% block title %}Series{% endblock %}
|
||||
{% block head_extra %}
|
||||
<style>
|
||||
dl {
|
||||
width: 210px;
|
||||
float: left;
|
||||
margin-right: 10px;
|
||||
}
|
||||
dt a {
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
font-size: smaller;
|
||||
}
|
||||
img {
|
||||
height: 200px;
|
||||
width: 200px;
|
||||
object-fit: cover;
|
||||
}
|
||||
dd .right {
|
||||
float: right;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block lists %}
|
||||
<div class="row">
|
||||
<div class="col-md">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-sm">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Series</th>
|
||||
<th scope="col">Episode</th>
|
||||
<th scope="col">Scrobbles</th>
|
||||
<th scope="col">All time</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for obj in object_list %}
|
||||
{% for video in obj.video_set.all %}
|
||||
<tr>
|
||||
<td><a href="{{video.get_absolute_url}}">{{video}}</a></td>
|
||||
<td><a href="{{obj.get_absolute_url}}">{{obj}}</a></td>
|
||||
<td>{{video.scrobble_set.count}}</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md">
|
||||
<div class="table-responsive">{% include "_scrobblable_list.html" %}</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
||||
@ -87,7 +87,7 @@ dd {
|
||||
<tbody>
|
||||
{% for scrobble in scrobbles.all %}
|
||||
<tr>
|
||||
<td>{{scrobble.local_timestamp}}</td>
|
||||
<td><a href={{scrobble.get_absolute_url}}>{{scrobble.local_timestamp}}</a></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
|
||||
32
vrobbler/templates/videos/video_list.html
Normal file
32
vrobbler/templates/videos/video_list.html
Normal file
@ -0,0 +1,32 @@
|
||||
{% extends "base_list.html" %}
|
||||
{% block title %}Videos{% endblock %}
|
||||
{% block head_extra %}
|
||||
<style>
|
||||
dl {
|
||||
width: 210px;
|
||||
float: left;
|
||||
margin-right: 10px;
|
||||
}
|
||||
dt a {
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
font-size: smaller;
|
||||
}
|
||||
img {
|
||||
height: 200px;
|
||||
width: 200px;
|
||||
object-fit: cover;
|
||||
}
|
||||
dd .right {
|
||||
float: right;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block lists %}
|
||||
<div class="row">
|
||||
<div class="col-md">
|
||||
<div class="table-responsive">{% include "_scrobblable_list.html" %}</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@ -48,12 +48,14 @@
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Date</th>
|
||||
<th scope="col">Notes</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for scrobble in scrobbles.all %}
|
||||
<tr>
|
||||
<td>{{scrobble.local_timestamp}}</td>
|
||||
<td><a href={{scrobble.get_absolute_url}}>{{scrobble.local_timestamp}}</a></td>
|
||||
<td>{% for note in scrobble.logdata.notes %}{{note}}{% if not forloop.last %}; {% endif%}{% endfor %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
|
||||
Reference in New Issue
Block a user