[templates] Fix navigation and add AGENTS file
All checks were successful
build & deploy / test (push) Successful in 1m48s
build & deploy / deploy (push) Successful in 21s

This commit is contained in:
2026-03-23 10:40:23 -04:00
parent 573321420d
commit d9a7a1cfd9
8 changed files with 227 additions and 3 deletions

3
AGENTS.md Normal file
View File

@ -0,0 +1,3 @@
This is a Django-based web application that has an API, but primarily functions with traditional Django views with HTML templates to display data that mostly constitutes "scrobbled" items. The app started as a way to track a user's watched videos via a Jellyfin server, but has since grown to keep track of a number of media types: music tracks, tasks, videos, web pages, food, life events, sports events, podcasts, video games, board games, beers, brick (lego) sets, puzzles, books and geolocations.
The project is written in Python and prefers to use "fat" models where logical methods are contained in either instance methods on instatiated data models, or classmethods on the Django model class itself. When logic grows too complex, helper functions should be pulled out into utils.py files and the model instance ro class method should call the utility function.

View File

@ -47,6 +47,42 @@
{% endblock %}
{% block lists %}
<div class="row mb-3">
<div class="col-auto">
{% if prev_period %}
<a href="{{ prev_period }}" class="btn btn-outline-secondary btn-sm">← Previous</a>
{% endif %}
</div>
<div class="col-auto text-center">
<span class="badge bg-secondary">{{ period_type|title }}</span>
</div>
<div class="col-auto">
{% if next_period %}
<a href="{{ next_period }}" class="btn btn-outline-secondary btn-sm">Next →</a>
{% endif %}
</div>
<div class="col-auto ms-auto">
{% if chart_years %}
<div class="btn-group btn-group-sm">
{% for y in chart_years|slice:":5" %}
<a href="/charts/?date={{ y }}" class="btn btn-sm {% if year == y %}btn-primary{% else %}btn-outline-secondary{% endif %}">{{ y }}</a>
{% endfor %}
</div>
{% endif %}
</div>
</div>
<div class="mb-2">
<small class="text-muted me-2">Months:</small>
{% for m in "1,2,3,4,5,6,7,8,9,10,11,12"|split:"," %}
<a href="/charts/?date={{ year }}-{{ m|stringformat:"02d" }}" class="btn btn-xs {% if month == m %}btn-primary{% else %}btn-outline-secondary{% endif %}" style="padding:1px 4px;font-size:10px;">{{ m }}</a>
{% endfor %}
<span class="text-muted mx-2">|</span>
<small class="text-muted me-2">Weeks:</small>
{% for w in "1,4,8,13,17,21,26,30,34,39,43,47,52"|split:"," %}
<a href="/charts/?date={{ year }}-W{{ w|stringformat:"02d" }}" class="btn btn-xs {% if week == w %}btn-primary{% else %}btn-outline-secondary{% endif %}" style="padding:1px 3px;font-size:9px;">{{ w }}</a>
{% endfor %}
<a href="#" class="btn btn-xs btn-outline-secondary" style="padding:1px 3px;font-size:9px;" onclick="var w=prompt('Week number (1-52):');if(w)this.href='/charts/?date={{ year }}-W'+w.padStart(2,'0');return false;">+</a>
</div>
{% if chart_type == "maloja" %}
{% include "scrobbles/_top_charts.html" %}
{% else %}

View File

@ -8,3 +8,8 @@ def get_item(dictionary, key):
if isinstance(dictionary, dict):
return dictionary.get(key)
return None
@register.filter
def split(value, arg):
return value.split(arg)

View File

@ -414,4 +414,145 @@ class ChartRecordView(TemplateView):
),
}
context["chart_years"] = self.get_available_years(user)
context["period_type"] = self.get_period_type()
context["prev_period"] = self.get_prev_period_url(user)
context["next_period"] = self.get_next_period_url(user)
return context
def get_available_years(self, user):
return list(
ChartRecord.objects.filter(user=user)
.values_list("year", flat=True)
.distinct()
.order_by("-year")
)
def get_period_type(self):
date_param = self.request.GET.get("date")
if not date_param:
return "current"
parts = date_param.split("-")
if len(parts) >= 3 and not parts[2].startswith("W"):
return "day"
if len(parts) >= 2 and parts[1].startswith("W"):
return "week"
if len(parts) >= 2:
return "month"
return "year"
def parse_date_params(self):
date_param = self.request.GET.get("date")
if not date_param:
return None, None, None, None
parts = date_param.split("-")
year = int(parts[0])
week = None
month = None
day = None
if len(parts) >= 2 and parts[1].startswith("W"):
week = int(parts[1].lstrip("W"))
elif len(parts) >= 2:
try:
month = int(parts[1])
except ValueError:
pass
if len(parts) >= 3:
if parts[2].startswith("W"):
week = int(parts[2].lstrip("W"))
else:
day = int(parts[2])
return year, month, week, day
def get_prev_period_url(self, user):
period_type = self.get_period_type()
if period_type == "current":
return None
year, month, week, day = self.parse_date_params()
if year is None:
return None
available_years = self.get_available_years(user)
if period_type == "day" and month and day:
if month == 1:
prev_month = 12
prev_year = year - 1
else:
prev_month = month - 1
prev_year = year
return f"/charts/?date={prev_year}-{prev_month:02d}"
elif period_type == "month" and month:
if month == 1:
prev_month = 12
prev_year = year - 1
else:
prev_month = month - 1
prev_year = year
return f"/charts/?date={prev_year}-{prev_month:02d}"
elif period_type == "week" and week:
if week == 1:
prev_week = 52
prev_year = year - 1
else:
prev_week = week - 1
prev_year = year
return f"/charts/?date={prev_year}-W{prev_week:02d}"
elif period_type == "year":
if year in available_years:
idx = available_years.index(year)
if idx + 1 < len(available_years):
return f"/charts/?date={available_years[idx + 1]}"
return f"/charts/?date={year - 1}"
return None
def get_next_period_url(self, user):
period_type = self.get_period_type()
if period_type == "current":
return None
year, month, week, day = self.parse_date_params()
if year is None:
return None
available_years = self.get_available_years(user)
if period_type == "day" and month and day:
if month == 12:
next_month = 1
next_year = year + 1
else:
next_month = month + 1
next_year = year
return f"/charts/?date={next_year}-{next_month:02d}"
elif period_type == "month" and month:
if month == 12:
next_month = 1
next_year = year + 1
else:
next_month = month + 1
next_year = year
return f"/charts/?date={next_year}-{next_month:02d}"
elif period_type == "week" and week:
if week == 52:
next_week = 1
next_year = year + 1
else:
next_week = week + 1
next_year = year
return f"/charts/?date={next_year}-W{next_week:02d}"
elif period_type == "year":
if year in available_years:
idx = available_years.index(year)
if idx > 0:
return f"/charts/?date={available_years[idx - 1]}"
return f"/charts/?date={year + 1}"
return None

View File

@ -1,3 +1,5 @@
from datetime import datetime
from django import template
register = template.Library()
@ -5,7 +7,11 @@ register = template.Library()
@register.filter(name="add_class")
def add_class(field, css_class):
# If the widget is CheckboxInput, skip adding 'form-control'
if field.field.widget.__class__.__name__ == "CheckboxInput":
return field.as_widget()
return field.as_widget(attrs={"class": css_class})
@register.simple_tag
def current_date(format):
return datetime.now().strftime(format)

View File

@ -28,6 +28,25 @@
{% if week %}<span class="btn btn-outline-secondary active">Week {{week}}</span>{% endif %}
{% if day %}<span class="btn btn-outline-secondary active">{{day}}</span>{% endif %}
</div>
<span class="ms-2">
{% if period_type == "day" %}
{% if prev_period %}<a href="{{ prev_period }}">&laquo; Last day</a>{% endif %}
{% if prev_period and next_period %} | {% endif %}
{% if next_period %}<a href="{{ next_period }}">Next day &raquo;</a>{% endif %}
{% elif period_type == "week" %}
{% if prev_period %}<a href="{{ prev_period }}">&laquo; Last week</a>{% endif %}
{% if prev_period and next_period %} | {% endif %}
{% if next_period %}<a href="{{ next_period }}">Next week &raquo;</a>{% endif %}
{% elif period_type == "month" %}
{% if prev_period %}<a href="{{ prev_period }}">&laquo; Last month</a>{% endif %}
{% if prev_period and next_period %} | {% endif %}
{% if next_period %}<a href="{{ next_period }}">Next month &raquo;</a>{% endif %}
{% elif period_type == "year" %}
{% if prev_period %}<a href="{{ prev_period }}">&laquo; Last year</a>{% endif %}
{% if prev_period and next_period %} | {% endif %}
{% if next_period %}<a href="{{ next_period }}">Next year &raquo;</a>{% endif %}
{% endif %}
</span>
</div>
</div>

View File

@ -1,8 +1,15 @@
{% load humanize %}
{% load naturalduration %}
{% load form_tags %}
<div class="row">
<div>
<p>Today <b>{{counts.today}}</b> | This Week <b>{{counts.week}}</b> | This Month <b>{{counts.month}}</b> | This Year <b>{{counts.year}}</b> | All Time <b>{{counts.alltime}}</b></p>
<p>
<a href="/charts/?date={% current_date "%Y-%m-%d" %}">Today</a> <b>{{counts.today}}</b> |
<a href="/charts/?date={% current_date "%Y" %}-W{% current_date "%W" %}">This Week</a> <b>{{counts.week}}</b> |
<a href="/charts/?date={% current_date "%Y-%m" %}">This Month</a> <b>{{counts.month}}</b> |
<a href="/charts/?date={% current_date "%Y" %}">This Year</a> <b>{{counts.year}}</b> |
<a href="/charts/">All Time</a> <b>{{counts.alltime}}</b>
</p>
</div>
</div>
<div class="row">

View File

@ -2,6 +2,7 @@
{% load static %}
{% load humanize %}
{% load naturalduration %}
{% load form_tags %}
{% block head_extra %}
<style>
@ -111,7 +112,13 @@
</div>
{% if not user.is_authenticated %}
<p>Today <b>{{counts.today}}</b> | This Week <b>{{counts.week}}</b> | This Month <b>{{counts.month}}</b> | This Year <b>{{counts.year}}</b> | All Time <b>{{counts.alltime}}</b></p>
<p>
<a href="/charts/?date={% current_date "%Y-%m-%d" %}">Today</a> <b>{{counts.today}}</b> |
<a href="/charts/?date={% current_date "%Y" %}-W{% current_date "%W" %}">This Week</a> <b>{{counts.week}}</b> |
<a href="/charts/?date={% current_date "%Y-%m" %}">This Month</a> <b>{{counts.month}}</b> |
<a href="/charts/?date={% current_date "%Y" %}">This Year</a> <b>{{counts.year}}</b> |
<a href="/charts/">All Time</a> <b>{{counts.alltime}}</b>
</p>
<canvas class="my-4 w-100" id="myChart" width="900" height="300"></canvas>
{% else %}
<div class="container">