2023-10-08 22:00:45 +00:00
|
|
|
from common.time import format_duration
|
2023-11-02 08:53:28 +00:00
|
|
|
from common.time import now as now_with_tz
|
|
|
|
from datetime import datetime, timedelta
|
2023-01-15 22:39:52 +00:00
|
|
|
from django.conf import settings
|
2023-11-01 19:18:39 +00:00
|
|
|
from django.db.models import Sum, F
|
2023-11-02 08:20:09 +00:00
|
|
|
from django.http import HttpResponseRedirect
|
2023-11-02 08:53:28 +00:00
|
|
|
from django.shortcuts import redirect, render
|
2023-11-02 08:20:09 +00:00
|
|
|
from django.urls import reverse
|
2023-11-02 08:53:28 +00:00
|
|
|
from zoneinfo import ZoneInfo
|
2023-01-15 22:39:52 +00:00
|
|
|
|
2023-02-18 20:12:18 +00:00
|
|
|
from .forms import (
|
|
|
|
GameForm,
|
|
|
|
PlatformForm,
|
|
|
|
PurchaseForm,
|
|
|
|
SessionForm,
|
|
|
|
EditionForm,
|
|
|
|
DeviceForm,
|
|
|
|
)
|
2023-02-18 19:49:46 +00:00
|
|
|
from .models import Game, Platform, Purchase, Session, Edition
|
2023-01-04 16:27:54 +00:00
|
|
|
|
|
|
|
|
2023-01-04 16:19:40 +00:00
|
|
|
def model_counts(request):
|
|
|
|
return {
|
|
|
|
"game_available": Game.objects.count() != 0,
|
2023-02-18 19:49:46 +00:00
|
|
|
"edition_available": Edition.objects.count() != 0,
|
2023-01-04 16:19:40 +00:00
|
|
|
"platform_available": Platform.objects.count() != 0,
|
|
|
|
"purchase_available": Purchase.objects.count() != 0,
|
|
|
|
"session_count": Session.objects.count(),
|
|
|
|
}
|
2022-12-31 13:18:27 +00:00
|
|
|
|
|
|
|
|
2023-11-02 08:52:42 +00:00
|
|
|
def stats_dropdown_year_range(request):
|
2023-11-02 14:27:19 +00:00
|
|
|
return {"stats_dropdown_year_range": range(2018, 2024)}
|
2023-11-02 08:52:42 +00:00
|
|
|
|
|
|
|
|
2022-12-31 13:18:27 +00:00
|
|
|
def add_session(request):
|
|
|
|
context = {}
|
2023-01-19 19:35:25 +00:00
|
|
|
initial = {}
|
|
|
|
|
2023-01-04 18:19:49 +00:00
|
|
|
now = now_with_tz()
|
2023-01-19 19:35:25 +00:00
|
|
|
initial["timestamp_start"] = now
|
|
|
|
|
2023-01-16 20:19:20 +00:00
|
|
|
last = Session.objects.all().last()
|
2023-01-19 19:35:25 +00:00
|
|
|
if last != None:
|
|
|
|
initial["purchase"] = last.purchase
|
|
|
|
|
2022-12-31 13:18:27 +00:00
|
|
|
form = SessionForm(request.POST or None, initial=initial)
|
|
|
|
if form.is_valid():
|
|
|
|
form.save()
|
2023-01-04 18:35:35 +00:00
|
|
|
return redirect("list_sessions")
|
2022-12-31 13:18:27 +00:00
|
|
|
|
2023-01-05 21:09:21 +00:00
|
|
|
context["title"] = "Add New Session"
|
2022-12-31 13:18:27 +00:00
|
|
|
context["form"] = form
|
2023-02-21 22:49:57 +00:00
|
|
|
return render(request, "add_session.html", context)
|
2022-12-31 13:18:27 +00:00
|
|
|
|
|
|
|
|
2023-01-04 18:19:49 +00:00
|
|
|
def update_session(request, session_id=None):
|
|
|
|
session = Session.objects.get(id=session_id)
|
|
|
|
session.finish_now()
|
|
|
|
session.save()
|
|
|
|
return redirect("list_sessions")
|
|
|
|
|
|
|
|
|
2023-01-30 16:38:44 +00:00
|
|
|
def edit_session(request, session_id=None):
|
|
|
|
context = {}
|
|
|
|
session = Session.objects.get(id=session_id)
|
|
|
|
form = SessionForm(request.POST or None, instance=session)
|
|
|
|
if form.is_valid():
|
|
|
|
form.save()
|
|
|
|
return redirect("list_sessions")
|
|
|
|
context["title"] = "Edit Session"
|
|
|
|
context["form"] = form
|
2023-09-18 18:21:05 +00:00
|
|
|
return render(request, "add_session.html", context)
|
2023-01-30 16:38:44 +00:00
|
|
|
|
|
|
|
|
2023-02-18 20:44:19 +00:00
|
|
|
def edit_purchase(request, purchase_id=None):
|
|
|
|
context = {}
|
|
|
|
purchase = Purchase.objects.get(id=purchase_id)
|
|
|
|
form = PurchaseForm(request.POST or None, instance=purchase)
|
|
|
|
if form.is_valid():
|
|
|
|
form.save()
|
|
|
|
return redirect("list_sessions")
|
|
|
|
context["title"] = "Edit Purchase"
|
|
|
|
context["form"] = form
|
|
|
|
return render(request, "add.html", context)
|
|
|
|
|
|
|
|
|
2023-02-20 16:16:19 +00:00
|
|
|
def edit_game(request, game_id=None):
|
|
|
|
context = {}
|
|
|
|
purchase = Game.objects.get(id=game_id)
|
|
|
|
form = GameForm(request.POST or None, instance=purchase)
|
|
|
|
if form.is_valid():
|
|
|
|
form.save()
|
|
|
|
return redirect("list_sessions")
|
|
|
|
context["title"] = "Edit Game"
|
|
|
|
context["form"] = form
|
|
|
|
return render(request, "add.html", context)
|
2023-10-01 19:28:02 +00:00
|
|
|
|
|
|
|
|
|
|
|
def view_game(request, game_id=None):
|
|
|
|
context = {}
|
|
|
|
game = Game.objects.get(id=game_id)
|
|
|
|
context["title"] = "View Game"
|
|
|
|
context["game"] = game
|
|
|
|
context["editions"] = Edition.objects.filter(game_id=game_id)
|
|
|
|
context["purchases"] = Purchase.objects.filter(edition__game_id=game_id)
|
2023-10-01 19:51:32 +00:00
|
|
|
context["sessions"] = Session.objects.filter(
|
|
|
|
purchase__edition__game_id=game_id
|
|
|
|
).order_by("-timestamp_start")
|
2023-10-13 14:32:12 +00:00
|
|
|
context["total_hours"] = float(
|
|
|
|
format_duration(context["sessions"].total_duration_unformatted(), "%2.1H")
|
2023-10-08 22:00:45 +00:00
|
|
|
)
|
|
|
|
context["session_average"] = round(
|
|
|
|
(context["total_hours"]) / int(context["sessions"].count()), 1
|
|
|
|
)
|
2023-10-01 19:51:32 +00:00
|
|
|
# here first and last is flipped
|
|
|
|
# because sessions are ordered from newest to oldest
|
|
|
|
# so the most recent are on top
|
|
|
|
context["last_session"] = context["sessions"].first()
|
|
|
|
context["first_session"] = context["sessions"].last()
|
2023-10-09 18:55:31 +00:00
|
|
|
context["sessions_with_notes"] = context["sessions"].exclude(note="")
|
2023-10-01 19:28:02 +00:00
|
|
|
return render(request, "view_game.html", context)
|
2023-02-20 16:16:19 +00:00
|
|
|
|
|
|
|
|
|
|
|
def edit_platform(request, platform_id=None):
|
|
|
|
context = {}
|
|
|
|
purchase = Platform.objects.get(id=platform_id)
|
|
|
|
form = PlatformForm(request.POST or None, instance=purchase)
|
|
|
|
if form.is_valid():
|
|
|
|
form.save()
|
|
|
|
return redirect("list_sessions")
|
|
|
|
context["title"] = "Edit Platform"
|
|
|
|
context["form"] = form
|
|
|
|
return render(request, "add.html", context)
|
|
|
|
|
|
|
|
|
2023-02-18 20:47:25 +00:00
|
|
|
def edit_edition(request, edition_id=None):
|
|
|
|
context = {}
|
|
|
|
edition = Edition.objects.get(id=edition_id)
|
|
|
|
form = EditionForm(request.POST or None, instance=edition)
|
|
|
|
if form.is_valid():
|
|
|
|
form.save()
|
|
|
|
return redirect("list_sessions")
|
|
|
|
context["title"] = "Edit Edition"
|
|
|
|
context["form"] = form
|
|
|
|
return render(request, "add.html", context)
|
|
|
|
|
|
|
|
|
2023-10-13 17:22:43 +00:00
|
|
|
def start_game_session(request, game_id: int):
|
|
|
|
last_session = (
|
|
|
|
Session.objects.filter(purchase__edition__game_id=game_id)
|
|
|
|
.order_by("-timestamp_start")
|
|
|
|
.first()
|
|
|
|
)
|
|
|
|
session = SessionForm(
|
|
|
|
{
|
|
|
|
"purchase": last_session.purchase.id,
|
|
|
|
"timestamp_start": now_with_tz(),
|
|
|
|
"device": last_session.device,
|
|
|
|
}
|
|
|
|
)
|
|
|
|
session.save()
|
|
|
|
return redirect("list_sessions")
|
|
|
|
|
|
|
|
|
|
|
|
def start_session_same_as_last(request, last_session_id: int):
|
2023-02-18 20:43:51 +00:00
|
|
|
last_session = Session.objects.get(id=last_session_id)
|
|
|
|
session = SessionForm(
|
|
|
|
{
|
|
|
|
"purchase": last_session.purchase.id,
|
|
|
|
"timestamp_start": now_with_tz(),
|
|
|
|
"device": last_session.device,
|
|
|
|
}
|
|
|
|
)
|
2023-01-13 15:54:24 +00:00
|
|
|
session.save()
|
|
|
|
return redirect("list_sessions")
|
|
|
|
|
|
|
|
|
2023-09-17 15:17:22 +00:00
|
|
|
# def delete_session(request, session_id=None):
|
|
|
|
# session = Session.objects.get(id=session_id)
|
|
|
|
# session.delete()
|
|
|
|
# return redirect("list_sessions")
|
2023-01-04 19:28:07 +00:00
|
|
|
|
|
|
|
|
2023-02-19 13:30:26 +00:00
|
|
|
def list_sessions(
|
|
|
|
request,
|
|
|
|
filter="",
|
|
|
|
purchase_id="",
|
|
|
|
platform_id="",
|
|
|
|
game_id="",
|
|
|
|
edition_id="",
|
|
|
|
ownership_type: str = "",
|
|
|
|
):
|
2022-12-31 13:18:27 +00:00
|
|
|
context = {}
|
2023-01-30 21:16:28 +00:00
|
|
|
context["title"] = "Sessions"
|
2023-01-03 18:03:30 +00:00
|
|
|
|
2023-01-15 22:14:28 +00:00
|
|
|
if filter == "purchase":
|
2023-01-03 18:03:30 +00:00
|
|
|
dataset = Session.objects.filter(purchase=purchase_id)
|
|
|
|
context["purchase"] = Purchase.objects.get(id=purchase_id)
|
2023-01-15 22:14:28 +00:00
|
|
|
elif filter == "platform":
|
|
|
|
dataset = Session.objects.filter(purchase__platform=platform_id)
|
|
|
|
context["platform"] = Platform.objects.get(id=platform_id)
|
2023-02-18 19:49:46 +00:00
|
|
|
elif filter == "edition":
|
|
|
|
dataset = Session.objects.filter(purchase__edition=edition_id)
|
|
|
|
context["edition"] = Edition.objects.get(id=edition_id)
|
2023-02-19 13:30:26 +00:00
|
|
|
elif filter == "game":
|
|
|
|
dataset = Session.objects.filter(purchase__edition__game=game_id)
|
|
|
|
context["game"] = Game.objects.get(id=game_id)
|
|
|
|
elif filter == "ownership_type":
|
|
|
|
dataset = Session.objects.filter(purchase__ownership_type=ownership_type)
|
|
|
|
context["ownership_type"] = dict(Purchase.OWNERSHIP_TYPES)[ownership_type]
|
2023-01-30 21:16:28 +00:00
|
|
|
elif filter == "recent":
|
2023-09-14 16:49:16 +00:00
|
|
|
current_year = datetime.now().year
|
|
|
|
first_day_of_year = datetime(current_year, 1, 1)
|
2023-01-30 21:16:28 +00:00
|
|
|
dataset = Session.objects.filter(
|
2023-09-14 16:49:16 +00:00
|
|
|
timestamp_start__gte=first_day_of_year
|
2023-01-31 15:37:44 +00:00
|
|
|
).order_by("-timestamp_start")
|
2023-09-14 16:49:16 +00:00
|
|
|
context["title"] = "This year"
|
2023-01-03 18:03:30 +00:00
|
|
|
else:
|
2023-01-18 15:52:40 +00:00
|
|
|
# by default, sort from newest to oldest
|
2023-01-15 22:20:43 +00:00
|
|
|
dataset = Session.objects.all().order_by("-timestamp_start")
|
2023-01-03 18:03:30 +00:00
|
|
|
|
2023-01-04 16:27:54 +00:00
|
|
|
for session in dataset:
|
2023-10-04 16:12:13 +00:00
|
|
|
if session.timestamp_end == None and session.duration_manual == timedelta(
|
|
|
|
seconds=0
|
|
|
|
):
|
2023-01-04 16:27:54 +00:00
|
|
|
session.timestamp_end = datetime.now(ZoneInfo(settings.TIME_ZONE))
|
|
|
|
session.unfinished = True
|
|
|
|
|
2023-10-08 22:00:45 +00:00
|
|
|
context["total_duration"] = dataset.total_duration_formatted()
|
2022-12-31 13:18:27 +00:00
|
|
|
context["dataset"] = dataset
|
2023-01-18 15:52:40 +00:00
|
|
|
# cannot use dataset[0] here because that might be only partial QuerySet
|
|
|
|
context["last"] = Session.objects.all().order_by("timestamp_start").last()
|
2022-12-31 13:18:27 +00:00
|
|
|
|
|
|
|
return render(request, "list_sessions.html", context)
|
|
|
|
|
|
|
|
|
2023-11-02 08:20:09 +00:00
|
|
|
def stats(request, year: int = 0):
|
|
|
|
selected_year = request.GET.get("year")
|
|
|
|
if selected_year:
|
|
|
|
return HttpResponseRedirect(reverse("stats_by_year", args=[selected_year]))
|
|
|
|
if year == 0:
|
|
|
|
year = now_with_tz().year
|
2023-11-01 19:18:39 +00:00
|
|
|
first_day_of_year = datetime(year, 1, 1)
|
2023-11-02 08:17:08 +00:00
|
|
|
last_day_of_year = datetime(year + 1, 1, 1)
|
|
|
|
year_sessions = Session.objects.filter(
|
|
|
|
timestamp_start__gte=first_day_of_year
|
|
|
|
).filter(timestamp_start__lt=last_day_of_year)
|
2023-11-01 19:18:39 +00:00
|
|
|
year_purchases = Purchase.objects.filter(session__in=year_sessions).distinct()
|
2023-11-02 14:08:11 +00:00
|
|
|
|
|
|
|
games_with_playtime = (
|
|
|
|
Game.objects.filter(edition__purchase__session__in=year_sessions)
|
|
|
|
.annotate(
|
|
|
|
total_playtime=Sum(
|
|
|
|
F("edition__purchase__session__duration_calculated")
|
|
|
|
+ F("edition__purchase__session__duration_manual")
|
|
|
|
)
|
2023-11-01 19:18:39 +00:00
|
|
|
)
|
2023-11-02 14:08:11 +00:00
|
|
|
.values("id", "name", "total_playtime")
|
2023-11-01 19:18:39 +00:00
|
|
|
)
|
2023-11-02 14:08:11 +00:00
|
|
|
top_10_games_by_playtime = games_with_playtime.order_by("-total_playtime")[:10]
|
|
|
|
for game in top_10_games_by_playtime:
|
|
|
|
game["formatted_playtime"] = format_duration(game["total_playtime"], "%2.0H")
|
2023-11-01 19:18:39 +00:00
|
|
|
|
|
|
|
total_playtime_per_platform = (
|
2023-11-02 14:09:31 +00:00
|
|
|
year_sessions.values("purchase__platform__name")
|
|
|
|
.annotate(total_playtime=Sum(F("duration_calculated") + F("duration_manual")))
|
|
|
|
.annotate(platform_name=F("purchase__platform__name"))
|
|
|
|
.values("platform_name", "total_playtime")
|
|
|
|
.order_by("-total_playtime")
|
2023-11-01 19:18:39 +00:00
|
|
|
)
|
|
|
|
for item in total_playtime_per_platform:
|
|
|
|
item["formatted_playtime"] = format_duration(item["total_playtime"], "%2.0H")
|
|
|
|
|
|
|
|
context = {
|
|
|
|
"total_hours": format_duration(
|
|
|
|
year_sessions.total_duration_unformatted(), "%2.0H"
|
|
|
|
),
|
|
|
|
"total_games": year_purchases.count(),
|
|
|
|
"total_2023_games": year_purchases.filter(edition__year_released=year).count(),
|
2023-11-02 14:08:11 +00:00
|
|
|
"top_10_games_by_playtime": top_10_games_by_playtime,
|
2023-11-01 19:18:39 +00:00
|
|
|
"year": year,
|
|
|
|
"total_playtime_per_platform": total_playtime_per_platform,
|
|
|
|
}
|
|
|
|
|
|
|
|
return render(request, "stats.html", context)
|
|
|
|
|
|
|
|
|
2022-12-31 13:18:27 +00:00
|
|
|
def add_purchase(request):
|
|
|
|
context = {}
|
|
|
|
now = datetime.now()
|
|
|
|
initial = {"date_purchased": now}
|
|
|
|
form = PurchaseForm(request.POST or None, initial=initial)
|
|
|
|
if form.is_valid():
|
|
|
|
form.save()
|
2023-02-18 19:50:36 +00:00
|
|
|
return redirect("index")
|
2022-12-31 13:18:27 +00:00
|
|
|
|
|
|
|
context["form"] = form
|
2023-01-03 21:04:36 +00:00
|
|
|
context["title"] = "Add New Purchase"
|
|
|
|
return render(request, "add.html", context)
|
2022-12-31 13:18:27 +00:00
|
|
|
|
|
|
|
|
|
|
|
def add_game(request):
|
|
|
|
context = {}
|
|
|
|
form = GameForm(request.POST or None)
|
|
|
|
if form.is_valid():
|
|
|
|
form.save()
|
2023-02-18 19:50:36 +00:00
|
|
|
return redirect("index")
|
2022-12-31 13:18:27 +00:00
|
|
|
|
|
|
|
context["form"] = form
|
|
|
|
context["title"] = "Add New Game"
|
|
|
|
return render(request, "add.html", context)
|
2023-01-04 16:22:36 +00:00
|
|
|
|
2023-01-04 16:23:34 +00:00
|
|
|
|
2023-02-18 19:49:46 +00:00
|
|
|
def add_edition(request):
|
|
|
|
context = {}
|
|
|
|
form = EditionForm(request.POST or None)
|
|
|
|
if form.is_valid():
|
|
|
|
form.save()
|
|
|
|
return redirect("index")
|
|
|
|
|
|
|
|
context["form"] = form
|
|
|
|
context["title"] = "Add New Edition"
|
2023-02-20 17:21:48 +00:00
|
|
|
return render(request, "add_edition.html", context)
|
2023-02-18 19:49:46 +00:00
|
|
|
|
|
|
|
|
2023-01-04 16:23:34 +00:00
|
|
|
def add_platform(request):
|
|
|
|
context = {}
|
|
|
|
form = PlatformForm(request.POST or None)
|
|
|
|
if form.is_valid():
|
|
|
|
form.save()
|
2023-02-18 19:50:36 +00:00
|
|
|
return redirect("index")
|
2023-01-04 16:23:34 +00:00
|
|
|
|
|
|
|
context["form"] = form
|
|
|
|
context["title"] = "Add New Platform"
|
|
|
|
return render(request, "add.html", context)
|
2023-02-18 19:50:36 +00:00
|
|
|
|
|
|
|
|
2023-02-18 20:12:18 +00:00
|
|
|
def add_device(request):
|
|
|
|
context = {}
|
|
|
|
form = DeviceForm(request.POST or None)
|
|
|
|
if form.is_valid():
|
|
|
|
form.save()
|
|
|
|
return redirect("index")
|
|
|
|
|
|
|
|
context["form"] = form
|
|
|
|
context["title"] = "Add New Device"
|
|
|
|
return render(request, "add.html", context)
|
|
|
|
|
|
|
|
|
2023-02-18 19:50:36 +00:00
|
|
|
def index(request):
|
|
|
|
return redirect("list_sessions_recent")
|