104 lines
3.6 KiB
Python
104 lines
3.6 KiB
Python
from datetime import datetime, timedelta
|
|
from typing import Any, Callable
|
|
|
|
from django.contrib.auth.decorators import login_required
|
|
from django.db.models import (
|
|
F,
|
|
Sum,
|
|
)
|
|
|
|
from django.http import HttpRequest, HttpResponse, HttpResponseRedirect
|
|
from django.shortcuts import redirect
|
|
from django.urls import reverse
|
|
from django.utils.timezone import localtime
|
|
from django.utils.timezone import now as timezone_now
|
|
|
|
from common.components import ExternalScript
|
|
from common.layout import render_page
|
|
from common.time import format_duration
|
|
from games.models import Game, Platform, Purchase, Session
|
|
from games.views.stats_content import stats_content
|
|
from games.views.stats_data import compute_stats
|
|
|
|
# Flowbite-datepicker UMD bundle, hoisted into the stats pages for YearPicker.
|
|
_STATS_SCRIPTS = ExternalScript(
|
|
"https://cdn.jsdelivr.net/npm/flowbite-datepicker@2.0.0/dist/Datepicker.umd.min.js"
|
|
)
|
|
|
|
|
|
def model_counts(request: HttpRequest) -> dict[str, bool]:
|
|
now = timezone_now()
|
|
# Use a contiguous [midnight, next midnight) range in the active timezone
|
|
# instead of day/month/year extracts: a range filter can use an index on
|
|
# timestamp_start, whereas the extracts force a per-row datetime function.
|
|
start_of_today = localtime(now).replace(hour=0, minute=0, second=0, microsecond=0)
|
|
start_of_tomorrow = start_of_today + timedelta(days=1)
|
|
today_played = Session.objects.filter(
|
|
timestamp_start__gte=start_of_today,
|
|
timestamp_start__lt=start_of_tomorrow,
|
|
).aggregate(time=Sum(F("duration_total")))["time"]
|
|
last_7_played = Session.objects.filter(
|
|
timestamp_start__gte=(now - timedelta(days=7))
|
|
).aggregate(time=Sum(F("duration_total")))["time"]
|
|
|
|
return {
|
|
"game_available": Game.objects.exists(),
|
|
"platform_available": Platform.objects.exists(),
|
|
"purchase_available": Purchase.objects.exists(),
|
|
"session_count": Session.objects.exists(),
|
|
"today_played": format_duration(today_played, "%H h %m m"),
|
|
"last_7_played": format_duration(last_7_played, "%H h %m m"),
|
|
}
|
|
|
|
|
|
def global_current_year(request: HttpRequest) -> dict[str, int]:
|
|
return {"global_current_year": datetime.now().year}
|
|
|
|
|
|
def use_custom_redirect(
|
|
func: Callable[..., HttpResponse],
|
|
) -> Callable[..., HttpResponse]:
|
|
"""
|
|
Will redirect to "return_path" session variable if set.
|
|
"""
|
|
|
|
def wrapper(request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse:
|
|
response = func(request, *args, **kwargs)
|
|
if isinstance(response, HttpResponseRedirect) and (
|
|
next_url := request.session.get("return_path")
|
|
):
|
|
return HttpResponseRedirect(next_url)
|
|
return response
|
|
|
|
return wrapper
|
|
|
|
|
|
@login_required
|
|
def stats_alltime(request: HttpRequest) -> HttpResponse:
|
|
request.session["return_path"] = request.path
|
|
data = compute_stats(None)
|
|
return render_page(
|
|
request, stats_content(data), title=data["title"], scripts=_STATS_SCRIPTS
|
|
)
|
|
|
|
|
|
@login_required
|
|
def stats(request: HttpRequest, year: int = 0) -> HttpResponse:
|
|
selected_year = request.GET.get("year")
|
|
if selected_year:
|
|
return HttpResponseRedirect(
|
|
reverse("games:stats_by_year", args=[selected_year])
|
|
)
|
|
if year == 0:
|
|
return HttpResponseRedirect(reverse("games:stats_alltime"))
|
|
request.session["return_path"] = request.path
|
|
data = compute_stats(year)
|
|
return render_page(
|
|
request, stats_content(data), title=data["title"], scripts=_STATS_SCRIPTS
|
|
)
|
|
|
|
|
|
@login_required
|
|
def index(request: HttpRequest) -> HttpResponse:
|
|
return redirect("games:list_sessions")
|