Improve time-related stuff
Add created_at to all models Add modified_at to Session Get rid of custom now() function Make sure aware datetime is used everywhere
This commit is contained in:
		| @ -1,12 +1,5 @@ | ||||
| import re | ||||
| from datetime import datetime, timedelta | ||||
| from zoneinfo import ZoneInfo | ||||
|  | ||||
| from django.conf import settings | ||||
|  | ||||
|  | ||||
| def now() -> datetime: | ||||
|     return datetime.now(ZoneInfo(settings.TIME_ZONE)) | ||||
| from datetime import timedelta | ||||
|  | ||||
|  | ||||
| def _safe_timedelta(duration: timedelta | int | None): | ||||
|  | ||||
| @ -0,0 +1,44 @@ | ||||
| # Generated by Django 4.1.5 on 2023-11-15 13:51 | ||||
|  | ||||
| from django.db import migrations, models | ||||
| import django.utils.timezone | ||||
|  | ||||
|  | ||||
| class Migration(migrations.Migration): | ||||
|  | ||||
|     dependencies = [ | ||||
|         ("games", "0030_alter_purchase_name"), | ||||
|     ] | ||||
|  | ||||
|     operations = [ | ||||
|         migrations.AddField( | ||||
|             model_name="device", | ||||
|             name="created_at", | ||||
|             field=models.DateTimeField(default=django.utils.timezone.now), | ||||
|         ), | ||||
|         migrations.AddField( | ||||
|             model_name="edition", | ||||
|             name="created_at", | ||||
|             field=models.DateTimeField(default=django.utils.timezone.now), | ||||
|         ), | ||||
|         migrations.AddField( | ||||
|             model_name="game", | ||||
|             name="created_at", | ||||
|             field=models.DateTimeField(default=django.utils.timezone.now), | ||||
|         ), | ||||
|         migrations.AddField( | ||||
|             model_name="platform", | ||||
|             name="created_at", | ||||
|             field=models.DateTimeField(default=django.utils.timezone.now), | ||||
|         ), | ||||
|         migrations.AddField( | ||||
|             model_name="purchase", | ||||
|             name="created_at", | ||||
|             field=models.DateTimeField(default=django.utils.timezone.now), | ||||
|         ), | ||||
|         migrations.AddField( | ||||
|             model_name="session", | ||||
|             name="created_at", | ||||
|             field=models.DateTimeField(default=django.utils.timezone.now), | ||||
|         ), | ||||
|     ] | ||||
| @ -0,0 +1,52 @@ | ||||
| # Generated by Django 4.1.5 on 2023-11-15 18:02 | ||||
|  | ||||
| from django.db import migrations, models | ||||
|  | ||||
|  | ||||
| class Migration(migrations.Migration): | ||||
|  | ||||
|     dependencies = [ | ||||
|         ("games", "0031_device_created_at_edition_created_at_game_created_at_and_more"), | ||||
|     ] | ||||
|  | ||||
|     operations = [ | ||||
|         migrations.AlterModelOptions( | ||||
|             name="session", | ||||
|             options={"get_latest_by": "timestamp_start"}, | ||||
|         ), | ||||
|         migrations.AddField( | ||||
|             model_name="session", | ||||
|             name="modified_at", | ||||
|             field=models.DateTimeField(auto_now=True), | ||||
|         ), | ||||
|         migrations.AlterField( | ||||
|             model_name="device", | ||||
|             name="created_at", | ||||
|             field=models.DateTimeField(auto_now_add=True), | ||||
|         ), | ||||
|         migrations.AlterField( | ||||
|             model_name="edition", | ||||
|             name="created_at", | ||||
|             field=models.DateTimeField(auto_now_add=True), | ||||
|         ), | ||||
|         migrations.AlterField( | ||||
|             model_name="game", | ||||
|             name="created_at", | ||||
|             field=models.DateTimeField(auto_now_add=True), | ||||
|         ), | ||||
|         migrations.AlterField( | ||||
|             model_name="platform", | ||||
|             name="created_at", | ||||
|             field=models.DateTimeField(auto_now_add=True), | ||||
|         ), | ||||
|         migrations.AlterField( | ||||
|             model_name="purchase", | ||||
|             name="created_at", | ||||
|             field=models.DateTimeField(auto_now_add=True), | ||||
|         ), | ||||
|         migrations.AlterField( | ||||
|             model_name="session", | ||||
|             name="created_at", | ||||
|             field=models.DateTimeField(auto_now_add=True), | ||||
|         ), | ||||
|     ] | ||||
| @ -1,10 +1,9 @@ | ||||
| from common.time import format_duration | ||||
| from datetime import datetime, timedelta | ||||
| from django.conf import settings | ||||
| from datetime import timedelta | ||||
| from django.db import models | ||||
| from django.core.exceptions import ValidationError | ||||
| from django.utils import timezone | ||||
| from django.db.models import F, Manager, Sum | ||||
| from zoneinfo import ZoneInfo | ||||
|  | ||||
|  | ||||
| class Game(models.Model): | ||||
| @ -12,6 +11,7 @@ class Game(models.Model): | ||||
|     sort_name = models.CharField(max_length=255, null=True, blank=True, default=None) | ||||
|     year_released = models.IntegerField(null=True, blank=True, default=None) | ||||
|     wikidata = models.CharField(max_length=50, null=True, blank=True, default=None) | ||||
|     created_at = models.DateTimeField(auto_now_add=True) | ||||
|  | ||||
|     def __str__(self): | ||||
|         return self.name | ||||
| @ -42,6 +42,7 @@ class Edition(models.Model): | ||||
|     ) | ||||
|     year_released = models.IntegerField(null=True, blank=True, default=None) | ||||
|     wikidata = models.CharField(max_length=50, null=True, blank=True, default=None) | ||||
|     created_at = models.DateTimeField(auto_now_add=True) | ||||
|  | ||||
|     def __str__(self): | ||||
|         return self.sort_name | ||||
| @ -128,6 +129,7 @@ class Purchase(models.Model): | ||||
|         blank=True, | ||||
|         related_name="related_purchases", | ||||
|     ) | ||||
|     created_at = models.DateTimeField(auto_now_add=True) | ||||
|  | ||||
|     def __str__(self): | ||||
|         additional_info = [ | ||||
| @ -156,6 +158,7 @@ class Purchase(models.Model): | ||||
| class Platform(models.Model): | ||||
|     name = models.CharField(max_length=255) | ||||
|     group = models.CharField(max_length=255, null=True, blank=True, default=None) | ||||
|     created_at = models.DateTimeField(auto_now_add=True) | ||||
|  | ||||
|     def __str__(self): | ||||
|         return self.name | ||||
| @ -173,6 +176,9 @@ class SessionQuerySet(models.QuerySet): | ||||
|  | ||||
|  | ||||
| class Session(models.Model): | ||||
|     class Meta: | ||||
|         get_latest_by = "timestamp_start" | ||||
|  | ||||
|     purchase = models.ForeignKey("Purchase", on_delete=models.CASCADE) | ||||
|     timestamp_start = models.DateTimeField() | ||||
|     timestamp_end = models.DateTimeField(blank=True, null=True) | ||||
| @ -186,6 +192,8 @@ class Session(models.Model): | ||||
|         default=None, | ||||
|     ) | ||||
|     note = models.TextField(blank=True, null=True) | ||||
|     created_at = models.DateTimeField(auto_now_add=True) | ||||
|     modified_at = models.DateTimeField(auto_now=True) | ||||
|  | ||||
|     objects = SessionQuerySet.as_manager() | ||||
|  | ||||
| @ -194,10 +202,10 @@ class Session(models.Model): | ||||
|         return f"{str(self.purchase)} {str(self.timestamp_start.date())} ({self.duration_formatted()}{mark})" | ||||
|  | ||||
|     def finish_now(self): | ||||
|         self.timestamp_end = datetime.now(ZoneInfo(settings.TIME_ZONE)) | ||||
|         self.timestamp_end = timezone.now() | ||||
|  | ||||
|     def start_now(): | ||||
|         self.timestamp_start = datetime.now(ZoneInfo(settings.TIME_ZONE)) | ||||
|         self.timestamp_start = timezone.now() | ||||
|  | ||||
|     def duration_seconds(self) -> timedelta: | ||||
|         manual = timedelta(0) | ||||
| @ -250,6 +258,7 @@ class Device(models.Model): | ||||
|     ] | ||||
|     name = models.CharField(max_length=255) | ||||
|     type = models.CharField(max_length=3, choices=DEVICE_TYPES, default=UNKNOWN) | ||||
|     created_at = models.DateTimeField(auto_now_add=True) | ||||
|  | ||||
|     def __str__(self): | ||||
|         return f"{self.name} ({self.get_type_display()})" | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| from common.time import format_duration, now as now_with_tz | ||||
| from common.time import format_duration | ||||
| from common.utils import safe_division | ||||
| from datetime import datetime, timedelta | ||||
| from django.conf import settings | ||||
| @ -7,6 +7,7 @@ from django.db.models.functions import TruncDate | ||||
| from django.http import HttpRequest, HttpResponse, HttpResponseRedirect | ||||
| from django.shortcuts import redirect, render | ||||
| from django.urls import reverse | ||||
| from django.utils import timezone | ||||
| from typing import Callable, Any | ||||
| from zoneinfo import ZoneInfo | ||||
|  | ||||
| @ -32,19 +33,15 @@ def model_counts(request): | ||||
|  | ||||
|  | ||||
| def stats_dropdown_year_range(request): | ||||
|     result = { | ||||
|         "stats_dropdown_year_range": range( | ||||
|             datetime.now(ZoneInfo(settings.TIME_ZONE)).year, 1999, -1 | ||||
|         ) | ||||
|     } | ||||
|     result = {"stats_dropdown_year_range": range(timezone.now().year, 1999, -1)} | ||||
|     return result | ||||
|  | ||||
|  | ||||
| def add_session(request, purchase_id=None): | ||||
|     context = {} | ||||
|     initial = {"timestamp_start": now_with_tz()} | ||||
|     initial = {"timestamp_start": timezone.now()} | ||||
|  | ||||
|     last = Session.objects.all().last() | ||||
|     last = Session.objects.last() | ||||
|     if last != None: | ||||
|         initial["purchase"] = last.purchase | ||||
|  | ||||
| @ -155,13 +152,11 @@ def view_game(request, game_id=None): | ||||
|         .order_by("year_released") | ||||
|     ) | ||||
|  | ||||
|     sessions = Session.objects.filter(purchase__edition__game=game).order_by( | ||||
|         "timestamp_start" | ||||
|     ) | ||||
|     sessions = Session.objects.filter(purchase__edition__game=game) | ||||
|     session_count = sessions.count() | ||||
|  | ||||
|     playrange_start = sessions.first().timestamp_start.strftime("%b %Y") | ||||
|     playrange_end = sessions.last().timestamp_start.strftime("%b %Y") | ||||
|     playrange_start = sessions.earliest().timestamp_start.strftime("%b %Y") | ||||
|     playrange_end = sessions.latest().timestamp_start.strftime("%b %Y") | ||||
|  | ||||
|     playrange = ( | ||||
|         playrange_start | ||||
| @ -225,15 +220,11 @@ def related_purchase_by_edition(request): | ||||
|  | ||||
| @use_custom_redirect | ||||
| def start_game_session(request, game_id: int): | ||||
|     last_session = ( | ||||
|         Session.objects.filter(purchase__edition__game_id=game_id) | ||||
|         .order_by("-timestamp_start") | ||||
|         .first() | ||||
|     ) | ||||
|     last_session = Session.objects.filter(purchase__edition__game_id=game_id).latest() | ||||
|     session = SessionForm( | ||||
|         { | ||||
|             "purchase": last_session.purchase.id, | ||||
|             "timestamp_start": now_with_tz(), | ||||
|             "timestamp_start": timezone.now(), | ||||
|             "device": last_session.device, | ||||
|         } | ||||
|     ) | ||||
| @ -246,7 +237,7 @@ def start_session_same_as_last(request, last_session_id: int): | ||||
|     session = SessionForm( | ||||
|         { | ||||
|             "purchase": last_session.purchase.id, | ||||
|             "timestamp_start": now_with_tz(), | ||||
|             "timestamp_start": timezone.now(), | ||||
|             "device": last_session.device, | ||||
|         } | ||||
|     ) | ||||
| @ -296,19 +287,18 @@ def list_sessions( | ||||
|         context["title"] = "This year" | ||||
|     else: | ||||
|         # by default, sort from newest to oldest | ||||
|         dataset = Session.objects.all().order_by("-timestamp_start") | ||||
|         dataset = Session.objects.order_by("-timestamp_start") | ||||
|  | ||||
|     for session in dataset: | ||||
|         if session.timestamp_end == None and session.duration_manual == timedelta( | ||||
|             seconds=0 | ||||
|         ): | ||||
|             session.timestamp_end = datetime.now(ZoneInfo(settings.TIME_ZONE)) | ||||
|             session.timestamp_end = timezone.now() | ||||
|             session.unfinished = True | ||||
|  | ||||
|     context["total_duration"] = dataset.total_duration_formatted() | ||||
|     context["dataset"] = dataset | ||||
|     # cannot use dataset[0] here because that might be only partial QuerySet | ||||
|     context["last"] = Session.objects.all().order_by("timestamp_start").last() | ||||
|     context["last"] = Session.objects.latest() | ||||
|  | ||||
|     return render(request, "list_sessions.html", context) | ||||
|  | ||||
| @ -318,7 +308,7 @@ def stats(request, year: int = 0): | ||||
|     if selected_year: | ||||
|         return HttpResponseRedirect(reverse("stats_by_year", args=[selected_year])) | ||||
|     if year == 0: | ||||
|         year = now_with_tz().year | ||||
|         year = timezone.now().year | ||||
|     this_year_sessions = Session.objects.filter(timestamp_start__year=year) | ||||
|     selected_currency = "CZK" | ||||
|     unique_days = ( | ||||
| @ -451,7 +441,7 @@ def stats(request, year: int = 0): | ||||
|  | ||||
| def add_purchase(request, edition_id=None): | ||||
|     context = {} | ||||
|     initial = {"date_purchased": now_with_tz()} | ||||
|     initial = {"date_purchased": timezone.now()} | ||||
|  | ||||
|     if request.method == "POST": | ||||
|         form = PurchaseForm(request.POST or None, initial=initial) | ||||
|  | ||||
		Reference in New Issue
	
	Block a user