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:
|
** TODO [#C] Allow users to see tasks on calendar view :vrobbler:personal:project:templates:feature:
|
||||||
https://codepen.io/oliviale/pen/QYqybo
|
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:
|
** 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]
|
* Version 23.0 [3/3]
|
||||||
** DONE Add dynamic forms for LogData classes :personal:feature:vrobbler:project:forms:logdata:
|
** DONE Add dynamic forms for LogData classes :personal:feature:vrobbler:project:forms:logdata:
|
||||||
:PROPERTIES:
|
: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 django_extensions.db.models import TimeStampedModel
|
||||||
from imagekit.models import ImageSpecField
|
from imagekit.models import ImageSpecField
|
||||||
from imagekit.processors import ResizeToFit
|
from imagekit.processors import ResizeToFit
|
||||||
from scrobbles.dataclasses import BaseLogData
|
from scrobbles.dataclasses import BaseLogData, WithPeopleLogData
|
||||||
from scrobbles.mixins import ScrobblableConstants, ScrobblableMixin
|
from scrobbles.mixins import ScrobblableConstants, ScrobblableMixin
|
||||||
|
|
||||||
BNULL = {"blank": True, "null": True}
|
BNULL = {"blank": True, "null": True}
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class FoodLogData(BaseLogData):
|
class FoodLogData(BaseLogData, WithPeopleLogData):
|
||||||
|
calories: Optional[int] = None
|
||||||
meal: Optional[str] = None
|
meal: Optional[str] = None
|
||||||
rating: Optional[str] = None
|
rating: Optional[str] = None
|
||||||
|
|
||||||
@ -48,6 +49,7 @@ class FoodCategory(TimeStampedModel):
|
|||||||
|
|
||||||
class Food(ScrobblableMixin):
|
class Food(ScrobblableMixin):
|
||||||
description = models.TextField(**BNULL)
|
description = models.TextField(**BNULL)
|
||||||
|
calories = models.IntegerField(**BNULL)
|
||||||
allrecipe_image = models.ImageField(upload_to="food/recipe/", **BNULL)
|
allrecipe_image = models.ImageField(upload_to="food/recipe/", **BNULL)
|
||||||
allrecipe_image_small = ImageSpecField(
|
allrecipe_image_small = ImageSpecField(
|
||||||
source="allrecipe_image",
|
source="allrecipe_image",
|
||||||
@ -72,7 +74,8 @@ class Food(ScrobblableMixin):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def subtitle(self):
|
def subtitle(self):
|
||||||
return self.category.name
|
if self.category:
|
||||||
|
return self.category.name
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def strings(self) -> ScrobblableConstants:
|
def strings(self) -> ScrobblableConstants:
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import Optional
|
|
||||||
from django.apps import apps
|
from django.apps import apps
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
|
|||||||
@ -1,11 +1,13 @@
|
|||||||
from decimal import Decimal, getcontext
|
|
||||||
import logging
|
import logging
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from decimal import Decimal
|
||||||
from typing import Dict
|
from typing import Dict
|
||||||
|
|
||||||
from django.contrib.auth import get_user_model
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
|
from scrobbles.dataclasses import BaseLogData, WithPeopleLogData
|
||||||
from scrobbles.mixins import ScrobblableConstants, ScrobblableMixin
|
from scrobbles.mixins import ScrobblableConstants, ScrobblableMixin
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@ -15,6 +17,9 @@ User = get_user_model()
|
|||||||
GEOLOC_ACCURACY = int(getattr(settings, "GEOLOC_ACCURACY", 4))
|
GEOLOC_ACCURACY = int(getattr(settings, "GEOLOC_ACCURACY", 4))
|
||||||
GEOLOC_PROXIMITY = Decimal(getattr(settings, "GEOLOC_PROXIMITY", "0.0001"))
|
GEOLOC_PROXIMITY = Decimal(getattr(settings, "GEOLOC_PROXIMITY", "0.0001"))
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class GeoLocationLogData(BaseLogData, WithPeopleLogData):
|
||||||
|
pass
|
||||||
|
|
||||||
class GeoLocation(ScrobblableMixin):
|
class GeoLocation(ScrobblableMixin):
|
||||||
COMPLETION_PERCENT = getattr(settings, "LOCATION_COMPLETION_PERCENT", 100)
|
COMPLETION_PERCENT = getattr(settings, "LOCATION_COMPLETION_PERCENT", 100)
|
||||||
@ -36,9 +41,13 @@ class GeoLocation(ScrobblableMixin):
|
|||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse(
|
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
|
@classmethod
|
||||||
def find_or_create(cls, data_dict: Dict) -> "GeoLocation":
|
def find_or_create(cls, data_dict: Dict) -> "GeoLocation":
|
||||||
"""Given a data dict from GPSLogger, does the heavy lifting of looking up
|
"""Given a data dict from GPSLogger, does the heavy lifting of looking up
|
||||||
|
|||||||
@ -8,11 +8,11 @@ urlpatterns = [
|
|||||||
path(
|
path(
|
||||||
"locations/",
|
"locations/",
|
||||||
views.GeoLocationListView.as_view(),
|
views.GeoLocationListView.as_view(),
|
||||||
name="geo_locations_list",
|
name="geolocation_list",
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
"locations/<slug:slug>/",
|
"locations/<slug:slug>/",
|
||||||
views.GeoLocationDetailView.as_view(),
|
views.GeoLocationDetailView.as_view(),
|
||||||
name="geo_location_detail",
|
name="geolocation_detail",
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|||||||
@ -42,7 +42,7 @@ class Mood(ScrobblableMixin):
|
|||||||
return str(self.uuid)
|
return str(self.uuid)
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse("moods:mood-detail", kwargs={"slug": self.uuid})
|
return reverse("moods:mood_detail", kwargs={"slug": self.uuid})
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def subtitle(self) -> str:
|
def subtitle(self) -> str:
|
||||||
|
|||||||
@ -5,10 +5,10 @@ app_name = "moods"
|
|||||||
|
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path("moods/", views.MoodListView.as_view(), name="mood-list"),
|
path("moods/", views.MoodListView.as_view(), name="mood_list"),
|
||||||
path(
|
path(
|
||||||
"moods/<slug:slug>/",
|
"moods/<slug:slug>/",
|
||||||
views.MoodDetailView.as_view(),
|
views.MoodDetailView.as_view(),
|
||||||
name="mood-detail",
|
name="mood_detail",
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|||||||
@ -573,7 +573,7 @@ class Album(TimeStampedModel):
|
|||||||
alt_name = name
|
alt_name = name
|
||||||
|
|
||||||
album = Album.objects.filter(
|
album = Album.objects.filter(
|
||||||
name=found_name, musicbrainz_id=album_dict.get("mbid")
|
musicbrainz_id=album_dict.get("mbid")
|
||||||
).first()
|
).first()
|
||||||
|
|
||||||
if not album:
|
if not album:
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
from django.views import generic
|
from django.views import generic
|
||||||
from podcasts.models import Podcast
|
from podcasts.models import Podcast
|
||||||
|
|
||||||
|
from scrobbles.views import ScrobbleableListView, ScrobbleableDetailView
|
||||||
|
|
||||||
class PodcastListView(generic.ListView):
|
class PodcastListView(generic.ListView):
|
||||||
model = Podcast
|
model = Podcast
|
||||||
|
|||||||
@ -3,8 +3,6 @@ from typing import Union, get_args, get_origin
|
|||||||
|
|
||||||
from django import forms
|
from django import forms
|
||||||
|
|
||||||
from people.models import Person
|
|
||||||
|
|
||||||
|
|
||||||
class ExportScrobbleForm(forms.Form):
|
class ExportScrobbleForm(forms.Form):
|
||||||
"""Provide options for downloading scrobbles"""
|
"""Provide options for downloading scrobbles"""
|
||||||
@ -76,10 +74,16 @@ def django_form_field_from_type(field_type, required=True):
|
|||||||
|
|
||||||
def form_from_dataclass(dataclass):
|
def form_from_dataclass(dataclass):
|
||||||
form_fields = {}
|
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):
|
for f in fields(dataclass):
|
||||||
if f.name in dataclass.override_fields():
|
print(f)
|
||||||
form_fields[f.name] = dataclass.override_fields()[f.name]
|
if f.name in override_fields:
|
||||||
|
form_fields[f.name] = override_fields[f.name]
|
||||||
continue
|
continue
|
||||||
|
|
||||||
required = f.default is None and f.default_factory is None
|
required = f.default is None and f.default_factory is None
|
||||||
|
|||||||
@ -655,6 +655,13 @@ class Scrobble(TimeStampedModel):
|
|||||||
(scrobble.elapsed_time)
|
(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
|
return scrobbles_by_type
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -892,8 +899,9 @@ class Scrobble(TimeStampedModel):
|
|||||||
if self.played_to_completion:
|
if self.played_to_completion:
|
||||||
if self.playback_position_seconds:
|
if self.playback_position_seconds:
|
||||||
return 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 self.media_obj.run_time_seconds
|
||||||
|
|
||||||
return (timezone.now() - self.timestamp).seconds
|
return (timezone.now() - self.timestamp).seconds
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -1070,6 +1078,8 @@ class Scrobble(TimeStampedModel):
|
|||||||
media_obj = self.puzzle
|
media_obj = self.puzzle
|
||||||
if self.task:
|
if self.task:
|
||||||
media_obj = self.task
|
media_obj = self.task
|
||||||
|
if self.food:
|
||||||
|
media_obj = self.food
|
||||||
return media_obj
|
return media_obj
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
|||||||
@ -47,8 +47,8 @@ class ScrobbleNtfyNotification(ScrobbleNotification):
|
|||||||
self.click_url = self.url_tmpl.format(path=self.scrobble.finish_url)
|
self.click_url = self.url_tmpl.format(path=self.scrobble.finish_url)
|
||||||
self.title = "Finish " + self.media_obj.strings.verb.lower() + "?"
|
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"):
|
if self.scrobble.log and isinstance(self.scrobble.log, dict) and self.scrobble.log.get("title"):
|
||||||
self.ntfy_str += f" - {self.scrobble.log.get('description')}"
|
self.ntfy_str += f" - {self.scrobble.log.get('title')}"
|
||||||
|
|
||||||
def send(self):
|
def send(self):
|
||||||
if (
|
if (
|
||||||
@ -72,7 +72,7 @@ class MoodNtfyNotification(BasicNtfyNotification):
|
|||||||
def __init__(self, profile, **kwargs):
|
def __init__(self, profile, **kwargs):
|
||||||
super().__init__(profile)
|
super().__init__(profile)
|
||||||
self.ntfy_str: str = "Would you like to check in about your mood?"
|
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!"
|
self.title = "Mood Check-in!"
|
||||||
|
|
||||||
def send(self):
|
def send(self):
|
||||||
|
|||||||
@ -368,7 +368,7 @@ def email_scrobble_board_game(
|
|||||||
enriched_game = find_and_enrich_board_game_data(game)
|
enriched_game = find_and_enrich_board_game_data(game)
|
||||||
if game.get("isBaseGame"):
|
if game.get("isBaseGame"):
|
||||||
base_games[game.get("id")] = enriched_game
|
base_games[game.get("id")] = enriched_game
|
||||||
elif game.get("isExpansion"):
|
if game.get("isExpansion"):
|
||||||
expansions[game.get("id")] = enriched_game
|
expansions[game.get("id")] = enriched_game
|
||||||
|
|
||||||
locations = {}
|
locations = {}
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import hashlib
|
|||||||
import logging
|
import logging
|
||||||
import re
|
import re
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
|
from typing import TYPE_CHECKING, Optional
|
||||||
from urllib.parse import urlparse
|
from urllib.parse import urlparse
|
||||||
from zoneinfo import ZoneInfo
|
from zoneinfo import ZoneInfo
|
||||||
|
|
||||||
@ -13,7 +14,10 @@ from django.utils import timezone
|
|||||||
from profiles.models import UserProfile
|
from profiles.models import UserProfile
|
||||||
from profiles.utils import now_user_timezone
|
from profiles.utils import now_user_timezone
|
||||||
from scrobbles.constants import LONG_PLAY_MEDIA
|
from scrobbles.constants import LONG_PLAY_MEDIA
|
||||||
from scrobbles.notifications import MoodNtfyNotification, ScrobbleNtfyNotification
|
from scrobbles.notifications import (
|
||||||
|
MoodNtfyNotification,
|
||||||
|
ScrobbleNtfyNotification,
|
||||||
|
)
|
||||||
from scrobbles.tasks import (
|
from scrobbles.tasks import (
|
||||||
process_koreader_import,
|
process_koreader_import,
|
||||||
process_lastfm_import,
|
process_lastfm_import,
|
||||||
@ -21,6 +25,9 @@ from scrobbles.tasks import (
|
|||||||
)
|
)
|
||||||
from webdav.client import get_webdav_client
|
from webdav.client import get_webdav_client
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from scrobbles.models import Scrobble
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
User = get_user_model()
|
User = get_user_model()
|
||||||
|
|
||||||
@ -345,3 +352,22 @@ def extract_domain(url):
|
|||||||
+ parsed_url.netloc.split(".")[-1]
|
+ parsed_url.netloc.split(".")[-1]
|
||||||
)
|
)
|
||||||
return domain
|
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"
|
slug_url_kwarg = "uuid"
|
||||||
|
|
||||||
def get_form_class(self):
|
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):
|
def get_form(self):
|
||||||
FormClass = self.get_form_class()
|
FormClass = self.get_form_class()
|
||||||
@ -969,11 +969,14 @@ class ScrobbleDetailView(DetailView):
|
|||||||
original_value = (self.object.log or {}).get(field_name)
|
original_value = (self.object.log or {}).get(field_name)
|
||||||
data[field_name] = original_value
|
data[field_name] = original_value
|
||||||
|
|
||||||
if "with_people_ids" in data:
|
if data.get("with_people_ids", False):
|
||||||
data["with_people_ids"] = [
|
data["with_people_ids"] = [
|
||||||
p.id for p in 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.log = data
|
||||||
self.object.save(update_fields=["log"])
|
self.object.save(update_fields=["log"])
|
||||||
return redirect(self.object.get_absolute_url())
|
return redirect(self.object.get_absolute_url())
|
||||||
|
|||||||
@ -82,7 +82,7 @@ class Task(LongPlayScrobblableMixin):
|
|||||||
|
|
||||||
def subtitle_for_user(self, user_id):
|
def subtitle_for_user(self, user_id):
|
||||||
scrobble = self.scrobbles(user_id).first()
|
scrobble = self.scrobbles(user_id).first()
|
||||||
return scrobble.logdata.description or ""
|
return scrobble.logdata.title or ""
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def find_or_create(cls, title: str) -> "Task":
|
def find_or_create(cls, title: str) -> "Task":
|
||||||
|
|||||||
@ -39,20 +39,13 @@ class VideoGameLogData(BaseLogData, LongPlayLogData, WithPeopleLogData):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def override_fields(cls) -> dict:
|
def override_fields(cls) -> dict:
|
||||||
fields = {}
|
return {
|
||||||
for base in cls.mro()[1:]:
|
|
||||||
if hasattr(base, "override_fields"):
|
|
||||||
base_fields = base.override_fields()
|
|
||||||
fields.update(base_fields)
|
|
||||||
custom_fields = {
|
|
||||||
"platform_id": forms.ModelChoiceField(
|
"platform_id": forms.ModelChoiceField(
|
||||||
queryset=VideoGamePlatform.objects.all(),
|
queryset=VideoGamePlatform.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
widget=forms.Select(),
|
widget=forms.Select(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
fields.update(custom_fields)
|
|
||||||
return fields
|
|
||||||
|
|
||||||
|
|
||||||
class VideoGamePlatform(TimeStampedModel):
|
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):
|
class VideoGameCollection(TimeStampedModel):
|
||||||
name = models.CharField(max_length=255)
|
name = models.CharField(max_length=255)
|
||||||
uuid = models.UUIDField(default=uuid4, editable=False, **BNULL)
|
uuid = models.UUIDField(default=uuid4, editable=False, **BNULL)
|
||||||
|
|||||||
@ -5,7 +5,6 @@ app_name = "videos"
|
|||||||
|
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
# path('', views.scrobble_endpoint, name='scrobble-list'),
|
|
||||||
path("movies/", views.MovieListView.as_view(), name="movie_list"),
|
path("movies/", views.MovieListView.as_view(), name="movie_list"),
|
||||||
path("series/", views.SeriesListView.as_view(), name="series_list"),
|
path("series/", views.SeriesListView.as_view(), name="series_list"),
|
||||||
path(
|
path(
|
||||||
@ -13,6 +12,7 @@ urlpatterns = [
|
|||||||
views.SeriesDetailView.as_view(),
|
views.SeriesDetailView.as_view(),
|
||||||
name="series_detail",
|
name="series_detail",
|
||||||
),
|
),
|
||||||
|
path('videos/', views.VideoListView.as_view(), name='video_list'),
|
||||||
path(
|
path(
|
||||||
"video/<slug:slug>/",
|
"video/<slug:slug>/",
|
||||||
views.VideoDetailView.as_view(),
|
views.VideoDetailView.as_view(),
|
||||||
|
|||||||
@ -67,10 +67,10 @@ class WebPage(ScrobblableMixin):
|
|||||||
self.save(update_fields=["extract"])
|
self.save(update_fields=["extract"])
|
||||||
|
|
||||||
def get_absolute_url(self):
|
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):
|
def get_read_url(self):
|
||||||
return reverse("webpages:webpage-read", kwargs={"slug": self.uuid})
|
return reverse("webpages:webpage_read", kwargs={"slug": self.uuid})
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def estimated_time_to_read_in_seconds(self):
|
def estimated_time_to_read_in_seconds(self):
|
||||||
|
|||||||
@ -5,15 +5,15 @@ app_name = "webpages"
|
|||||||
|
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path("webpages/", views.WebPageListView.as_view(), name="webpage-list"),
|
path("webpages/", views.WebPageListView.as_view(), name="webpage_list"),
|
||||||
path(
|
path(
|
||||||
"webpages/<slug:slug>/",
|
"webpages/<slug:slug>/",
|
||||||
views.WebPageDetailView.as_view(),
|
views.WebPageDetailView.as_view(),
|
||||||
name="webpage-detail",
|
name="webpage_detail",
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
"webpage/<slug:slug>/read/",
|
"webpage/<slug:slug>/read/",
|
||||||
views.WebPageReadView.as_view(),
|
views.WebPageReadView.as_view(),
|
||||||
name="webpage-read",
|
name="webpage_read",
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|||||||
@ -32,7 +32,7 @@ class WebPageReadView(
|
|||||||
if latest_scrobble.played_to_completion:
|
if latest_scrobble.played_to_completion:
|
||||||
redirect(
|
redirect(
|
||||||
reverse(
|
reverse(
|
||||||
"webpages:webpage-detail", kwargs={"slug": webpage.uuid}
|
"webpages:webpage_detail", kwargs={"slug": webpage.uuid}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
return super().get(*args, **kwargs)
|
return super().get(*args, **kwargs)
|
||||||
|
|||||||
@ -1,8 +1,10 @@
|
|||||||
{% load urlreplace %}
|
{% load urlreplace %}
|
||||||
|
{% load naturalduration %}
|
||||||
|
|
||||||
<table class="table table-striped table-sm">
|
<table class="table table-striped table-sm">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
|
<th scope="col">Latest</th>
|
||||||
<th scope="col">Title</th>
|
<th scope="col">Title</th>
|
||||||
<th scope="col">Scrobbles</th>
|
<th scope="col">Scrobbles</th>
|
||||||
<th scope="col">Start</th>
|
<th scope="col">Start</th>
|
||||||
@ -11,6 +13,7 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
{% for obj in object_list %}
|
{% for obj in object_list %}
|
||||||
<tr>
|
<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>
|
<td><a href="{{obj.get_absolute_url}}">{{obj}}</a></td>
|
||||||
{% if request.user.is_authenticated %}
|
{% if request.user.is_authenticated %}
|
||||||
<td>{{obj.scrobble_count}}</td>
|
<td>{{obj.scrobble_count}}</td>
|
||||||
|
|||||||
@ -175,7 +175,7 @@
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<header class="navbar navbar-dark sticky-top bg-dark flex-md-nowrap p-0 shadow">
|
<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">
|
<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>
|
<span class="navbar-toggler-icon"></span>
|
||||||
</button>
|
</button>
|
||||||
@ -208,85 +208,8 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
{% endif %}
|
{% 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 %}
|
{% block extra_nav %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
<hr/>
|
|
||||||
|
|
||||||
{% if now_playing_list and user.is_authenticated %}
|
{% if now_playing_list and user.is_authenticated %}
|
||||||
<ul>
|
<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 %}
|
{% 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>
|
<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.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>
|
<p><small>{{scrobble.local_timestamp|naturaltime}} from {{scrobble.source}}</small></p>
|
||||||
<div class="progress-bar" style="margin-right:5px;">
|
<div class="progress-bar" style="margin-right:5px;">
|
||||||
<span class="progress-bar-fill" style="width: {{scrobble.percent_played}}%;"></span>
|
<span class="progress-bar-fill" style="width: {{scrobble.percent_played}}%;"></span>
|
||||||
|
|||||||
@ -55,7 +55,7 @@
|
|||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="col">Date</th>
|
<th scope="col">Date</th>
|
||||||
<th scope="col">Publisher</th>
|
<th scope="col">Location</th>
|
||||||
<th scope="col">Players</th>
|
<th scope="col">Players</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
@ -63,7 +63,7 @@
|
|||||||
{% for scrobble in scrobbles.all|dictsortreversed:"timestamp" %}
|
{% for scrobble in scrobbles.all|dictsortreversed:"timestamp" %}
|
||||||
<tr>
|
<tr>
|
||||||
<td><a href={{scrobble.get_absolute_url}}>{{scrobble.local_timestamp}}</a></td>
|
<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>
|
<td>{% if scrobble.logdata.player_log %}{{scrobble.logdata.player_log}}{% else %}No data{% endif %}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|||||||
@ -45,7 +45,7 @@
|
|||||||
{% for scrobble in scrobbles.all|dictsortreversed:"timestamp" %}
|
{% for scrobble in scrobbles.all|dictsortreversed:"timestamp" %}
|
||||||
<tr>
|
<tr>
|
||||||
<td><a href="{{scrobble.get_absolute_url}}">{{scrobble.local_timestamp}}</a></td>
|
<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>{% 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>
|
<td>{% for author in scrobble.book.authors.all %}<a href="{{author.get_absolute_url}}">{{author}}</a>{% if not forloop.last %}, {% endif %}{% endfor %}</td>
|
||||||
</tr>
|
</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>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="col">Date</th>
|
<th scope="col">Date</th>
|
||||||
|
<th scope="col">With</th>
|
||||||
|
<th scope="col">Notes</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for scrobble in scrobbles.all|dictsortreversed:"timestamp" %}
|
{% for scrobble in object.scrobble_set.all|dictsortreversed:"timestamp" %}
|
||||||
<tr>
|
<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>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|||||||
@ -67,13 +67,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for location in object_list %}
|
{% include "_scrobblable_list.html" %}
|
||||||
<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 %}
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -28,7 +28,7 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
{% for scrobble in scrobbles.all %}
|
{% for scrobble in scrobbles.all %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{scrobble.local_timestamp}}</td>
|
<td><a href={{scrobble.get_absolute_url}}>{{scrobble.local_timestamp}}</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|||||||
@ -82,7 +82,7 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
{% for scrobble in object.scrobbles %}
|
{% for scrobble in object.scrobbles %}
|
||||||
<tr>
|
<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.get_absolute_url}}">{{scrobble.track.title}}</a></td>
|
||||||
<td><a href="{{scrobble.track.artist.get_absolute_url}}">{{scrobble.track.artist.name}}</a></td>
|
<td><a href="{{scrobble.track.artist.get_absolute_url}}">{{scrobble.track.artist.name}}</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
@ -83,7 +83,7 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
{% for scrobble in object.scrobbles %}
|
{% for scrobble in object.scrobbles %}
|
||||||
<tr>
|
<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.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>
|
<td><a href="{{scrobble.track.primary_album.get_absolute_url}}">{{scrobble.track.primary_album.name}}</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
@ -28,7 +28,7 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
{% for scrobble in scrobbles.all %}
|
{% for scrobble in scrobbles.all %}
|
||||||
<tr>
|
<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.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.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>
|
<td><a href="{{scrobble.track.artist.get_absolute_url}}">{{scrobble.track.artist}}</a></td>
|
||||||
|
|||||||
@ -20,40 +20,7 @@
|
|||||||
<hr />
|
<hr />
|
||||||
|
|
||||||
<div class="col-md">
|
<div class="col-md">
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">{% include "_scrobblable_list.html" %}</div>
|
||||||
<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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@ -45,7 +45,7 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
{% for scrobble in scrobbles.all %}
|
{% for scrobble in scrobbles.all %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{scrobble.local_timestamp}}</td>
|
<td><a href={{scrobble.get_absolute_url}}>{{scrobble.local_timestamp}}</a></td>
|
||||||
<td>{{scrobble.podcast_episode}}</td>
|
<td>{{scrobble.podcast_episode}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
{% extends "base_list.html" %}
|
{% extends "base_list.html" %}
|
||||||
|
|
||||||
|
{% block title %}Podcasts{% endblock %}
|
||||||
{% block lists %}
|
{% block lists %}
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md">
|
<div class="col-md">
|
||||||
@ -7,20 +8,19 @@
|
|||||||
<table class="table table-striped table-sm">
|
<table class="table table-striped table-sm">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="col">Series</th>
|
|
||||||
<th scope="col">Episode</th>
|
<th scope="col">Episode</th>
|
||||||
|
<th scope="col">Podcast</th>
|
||||||
<th scope="col">Scrobbles</th>
|
<th scope="col">Scrobbles</th>
|
||||||
<th scope="col">All time</th>
|
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for obj in object_list %}
|
{% for obj in object_list.all %}
|
||||||
{% for episode in obj.episode_set.all %}
|
{{obj.episodes}}
|
||||||
|
{% for episode in obj.podcastepisode_set.all %}
|
||||||
<tr>
|
<tr>
|
||||||
<td><a href="{{episode.get_absolute_url}}">{{episode}}</a></td>
|
<td><a href="{{episode.get_absolute_url}}">{{episode}}</a></td>
|
||||||
<td><a href="{{obj.get_absolute_url}}">{{obj}}</a></td>
|
<td><a href="{{obj.get_absolute_url}}">{{obj}}</a></td>
|
||||||
<td>{{episode.scrobble_set.count}}</td>
|
<td>{{episode.scrobble_set.count}}</td>
|
||||||
<td></td>
|
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|||||||
@ -57,7 +57,7 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
{% for scrobble in scrobbles.all|dictsortreversed:"timestamp" %}
|
{% for scrobble in scrobbles.all|dictsortreversed:"timestamp" %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{scrobble.local_timestamp}}</td>
|
<td><a href={{scrobble.get_absolute_url}}>{{scrobble.local_timestamp}}</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|||||||
@ -7,90 +7,141 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md">
|
<div class="col-md">
|
||||||
|
<h3><a href="{% url 'music:tracks_list' %}">Tracks</a></h3>
|
||||||
{% if Track %}
|
{% if Track %}
|
||||||
<h2>Music</h2>
|
|
||||||
{% with scrobbles=Track count=Track_count time=Track_time %}
|
{% with scrobbles=Track count=Track_count time=Track_time %}
|
||||||
{% include "scrobbles/_scrobble_table.html" %}
|
{% include "scrobbles/_scrobble_table.html" %}
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
|
{% else %}
|
||||||
|
<p>No tracks today</p>
|
||||||
{% endif %}
|
{% 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>
|
||||||
<div class="col-md">
|
<div class="col-md">
|
||||||
|
|
||||||
|
<h3><a href="{% url 'tasks:task_list' %}">Tasks</a></h3>
|
||||||
{% if Task %}
|
{% if Task %}
|
||||||
<h2>Latest tasks</h2>
|
|
||||||
{% with scrobbles=Task count=Task_count time=Task_time %}
|
{% with scrobbles=Task count=Task_count time=Task_time %}
|
||||||
{% include "scrobbles/_scrobble_table.html" %}
|
{% include "scrobbles/_scrobble_table.html" %}
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
|
{% else %}
|
||||||
|
<p>No tasks today</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
<h3><a href="{% url 'videos:video_list' %}">Videos</a></h3>
|
||||||
{% if Video %}
|
{% if Video %}
|
||||||
<h2>Videos</h2>
|
|
||||||
{% with scrobbles=Video count=Video_count time=Video_time %}
|
{% with scrobbles=Video count=Video_count time=Video_time %}
|
||||||
{% include "scrobbles/_scrobble_table.html" %}
|
{% include "scrobbles/_scrobble_table.html" %}
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
|
{% else %}
|
||||||
|
<p>No videos today</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
<h3><a href="{% url 'webpages:webpage_list' %}">Web pages</a></h3>
|
||||||
{% if WebPage %}
|
{% if WebPage %}
|
||||||
<h4>Web pages</h4>
|
|
||||||
{% with scrobbles=WebPage count=WebPage_count time=WebPage_time %}
|
{% with scrobbles=WebPage count=WebPage_count time=WebPage_time %}
|
||||||
{% include "scrobbles/_scrobble_table.html" %}
|
{% include "scrobbles/_scrobble_table.html" %}
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
|
{% else%}
|
||||||
|
<p>No web page read today</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
<h3><a href="{% url 'sports:event_list' %}">Sport events</a></h3>
|
||||||
{% if SportEvent %}
|
{% if SportEvent %}
|
||||||
<h2>Sports</h2>
|
|
||||||
{% with scrobbles=SportEvent count=SportEvent_count time=SportEvent_time %}
|
{% with scrobbles=SportEvent count=SportEvent_count time=SportEvent_time %}
|
||||||
{% include "scrobbles/_scrobble_table.html" %}
|
{% include "scrobbles/_scrobble_table.html" %}
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
|
{% else %}
|
||||||
|
<p>No sports today</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
<h3><a href="{% url 'podcasts:podcast_list' %}">Podcasts</a></h3>
|
||||||
{% if PodcastEpisode %}
|
{% if PodcastEpisode %}
|
||||||
<h2>Latest podcasts</h2>
|
|
||||||
{% with scrobbles=PodcastEpisode count=PodcastEpisode_count time=PodcastEpisode_time %}
|
{% with scrobbles=PodcastEpisode count=PodcastEpisode_count time=PodcastEpisode_time %}
|
||||||
{% include "scrobbles/_scrobble_table.html" %}
|
{% include "scrobbles/_scrobble_table.html" %}
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
|
{% else %}
|
||||||
|
<p>No podcasts today</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
<h3><a href="{% url "videogames:videogame_list" %}">Video games</a></h3>
|
||||||
{% if VideoGame %}
|
{% if VideoGame %}
|
||||||
<h4>Video games</h4>
|
|
||||||
{% with scrobbles=VideoGame count=VideoGame_count time=VideoGame_time %}
|
{% with scrobbles=VideoGame count=VideoGame_count time=VideoGame_time %}
|
||||||
{% include "scrobbles/_scrobble_table.html" %}
|
{% include "scrobbles/_scrobble_table.html" %}
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
|
{% else %}
|
||||||
|
<p>No video games today</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
<h3><a href="{% url "boardgames:boardgame_list" %}">Board games</a></h3>
|
||||||
{% if BoardGame %}
|
{% if BoardGame %}
|
||||||
<h4>Board games</h4>
|
|
||||||
{% with scrobbles=BoardGame count=BoardGame_count time=BoardGame_time %}
|
{% with scrobbles=BoardGame count=BoardGame_count time=BoardGame_time %}
|
||||||
{% include "scrobbles/_scrobble_table.html" %}
|
{% include "scrobbles/_scrobble_table.html" %}
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
|
{% else %}
|
||||||
|
<p>No board games today</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
<h3><a href="{% url 'beers:beer_list' %}">Beers</a></h3>
|
||||||
{% if Beer %}
|
{% if Beer %}
|
||||||
<h4>Beers</h4>
|
|
||||||
{% with scrobbles=Beer count=Beer_count time=Beer_time %}
|
{% with scrobbles=Beer count=Beer_count time=Beer_time %}
|
||||||
{% include "scrobbles/_scrobble_table.html" %}
|
{% include "scrobbles/_scrobble_table.html" %}
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
|
<p>No beer today</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
<h3><a href="{% url 'bricksets:brickset_list' %}">Brick sets</a></h3>
|
||||||
{% if BrickSet %}
|
{% if BrickSet %}
|
||||||
<h4>Brick sets</h4>
|
|
||||||
{% with scrobbles=BrickSet count=BrickSet_count time=BrickSet_time %}
|
{% with scrobbles=BrickSet count=BrickSet_count time=BrickSet_time %}
|
||||||
{% include "scrobbles/_scrobble_table.html" %}
|
{% include "scrobbles/_scrobble_table.html" %}
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
|
{% else %}
|
||||||
|
<p>No brick sets today</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
<h3><a href="{% url 'puzzles:puzzle_list' %}">Puzzles</a></h3>
|
||||||
{% if Puzzle %}
|
{% if Puzzle %}
|
||||||
<h4>Puzzles</h4>
|
|
||||||
{% with scrobbles=Puzzle count=Puzzle_count time=Puzzle_time %}
|
{% with scrobbles=Puzzle count=Puzzle_count time=Puzzle_time %}
|
||||||
{% include "scrobbles/_scrobble_table.html" %}
|
{% include "scrobbles/_scrobble_table.html" %}
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
|
{% else %}
|
||||||
|
<p>No puzzles today</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
<h3><a href="{% url 'books:book_list' %}">Books</a></h3>
|
||||||
{% if Book %}
|
{% if Book %}
|
||||||
<h4>Books</h4>
|
|
||||||
{% with scrobbles=Book count=Book_count time=Book_time %}
|
{% with scrobbles=Book count=Book_count time=Book_time %}
|
||||||
{% include "scrobbles/_scrobble_table.html" %}
|
{% include "scrobbles/_scrobble_table.html" %}
|
||||||
{% endwith %}
|
{% 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 %}
|
{% endif %}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
{% load humanize %}
|
{% load humanize %}
|
||||||
{% load naturalduration %}
|
{% load naturalduration %}
|
||||||
<tr {% if scrobble.in_progress %}class="in-progress"{% endif %}>
|
<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>
|
<td>
|
||||||
{% if scrobble.media_type == "Task" %}
|
{% 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 %}
|
{% else %}
|
||||||
<a href="{{scrobble.media_obj.get_absolute_url}}">{{scrobble.media_obj|truncatechars_html:45}}</a>
|
<a href="{{scrobble.media_obj.get_absolute_url}}">{{scrobble.media_obj|truncatechars_html:45}}</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|||||||
@ -12,7 +12,9 @@
|
|||||||
<h1>{{ object.media_obj }} - {{object.media_type}}</h1>
|
<h1>{{ object.media_obj }} - {{object.media_type}}</h1>
|
||||||
|
|
||||||
<!-- Your existing detail page content -->
|
<!-- Your existing detail page content -->
|
||||||
|
{% if object.logdata.avg_seconds_per_page %}
|
||||||
<p>Rate: {{object.logdata.avg_seconds_per_page}}s per page</p>
|
<p>Rate: {{object.logdata.avg_seconds_per_page}}s per page</p>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
<h2>Edit Log</h2>
|
<h2>Edit Log</h2>
|
||||||
<form method="post" class="needs-validation" novalidate>
|
<form method="post" class="needs-validation" novalidate>
|
||||||
|
|||||||
@ -56,6 +56,14 @@
|
|||||||
<h1 class="h2">{% if date %}{{date|naturaltime}}{% else %}{{title}}{% 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">
|
||||||
|
<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">
|
<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 %}
|
||||||
<form action="{% url 'scrobbles:lastfm-import' %}" method="get">
|
<form action="{% url 'scrobbles:lastfm-import' %}" method="get">
|
||||||
|
|||||||
@ -20,7 +20,7 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
{% for scrobble in scrobbles.all %}
|
{% for scrobble in scrobbles.all %}
|
||||||
<tr>
|
<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.name}}</td>
|
||||||
<td>{{scrobble.media_obj.round.season.league}}</td>
|
<td>{{scrobble.media_obj.round.season.league}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
@ -54,7 +54,7 @@
|
|||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="col">Date</th>
|
<th scope="col">Date</th>
|
||||||
<th scope="col">Description</th>
|
<th scope="col">Title</th>
|
||||||
<th scope="col">Notes</th>
|
<th scope="col">Notes</th>
|
||||||
<th scope="col">Source</th>
|
<th scope="col">Source</th>
|
||||||
</tr>
|
</tr>
|
||||||
@ -63,7 +63,7 @@
|
|||||||
{% for scrobble in scrobbles.all|dictsortreversed:"timestamp" %}
|
{% for scrobble in scrobbles.all|dictsortreversed:"timestamp" %}
|
||||||
<tr>
|
<tr>
|
||||||
<td><a href="{{scrobble.get_absolute_url}}">{{scrobble.local_timestamp}}</a></td>
|
<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.logdata.notes_as_str|safe}}</td>
|
||||||
<td>{{scrobble.source}}</td>
|
<td>{{scrobble.source}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
@ -57,7 +57,7 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
{% for scrobble in object.scrobble_set.all|dictsortreversed:"timestamp" %}
|
{% for scrobble in object.scrobble_set.all|dictsortreversed:"timestamp" %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{scrobble.local_timestamp}}</td>
|
<td><a href={{scrobble.get_absolute_url}}>{{scrobble.local_timestamp}}</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|||||||
@ -1,28 +1,32 @@
|
|||||||
{% extends "base_list.html" %}
|
{% 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 %}
|
{% block lists %}
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md">
|
<div class="col-md">
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">{% include "_scrobblable_list.html" %}</div>
|
||||||
<table class="table table-striped table-sm">
|
</div>
|
||||||
<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>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@ -60,7 +60,7 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
{% for scrobble in scrobbles %}
|
{% for scrobble in scrobbles %}
|
||||||
<tr>
|
<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><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.season_number}}</td>
|
||||||
<td>{{scrobble.media_obj.episode_number}}</td>
|
<td>{{scrobble.media_obj.episode_number}}</td>
|
||||||
|
|||||||
@ -1,32 +1,32 @@
|
|||||||
{% extends "base_list.html" %}
|
{% 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 %}
|
{% block lists %}
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md">
|
<div class="col-md">
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">{% include "_scrobblable_list.html" %}</div>
|
||||||
<table class="table table-striped table-sm">
|
</div>
|
||||||
<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>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@ -87,7 +87,7 @@ dd {
|
|||||||
<tbody>
|
<tbody>
|
||||||
{% for scrobble in scrobbles.all %}
|
{% for scrobble in scrobbles.all %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{scrobble.local_timestamp}}</td>
|
<td><a href={{scrobble.get_absolute_url}}>{{scrobble.local_timestamp}}</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</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>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="col">Date</th>
|
<th scope="col">Date</th>
|
||||||
|
<th scope="col">Notes</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for scrobble in scrobbles.all %}
|
{% for scrobble in scrobbles.all %}
|
||||||
<tr>
|
<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>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|||||||
Reference in New Issue
Block a user