From 447bd4820c398e09ce572d3797289b6eb0f89002 Mon Sep 17 00:00:00 2001 From: Lukas Kucharczyk Date: Thu, 8 Aug 2024 14:47:51 +0200 Subject: [PATCH 01/45] reformat with djlint --reformat --- games/templates/add_purchase.html | 4 +- games/templates/base.html | 74 ++++++------ games/templates/components/game_link.html | 5 +- games/templates/components/popover.html | 11 +- games/templates/list_sessions.html | 77 ++++++------ games/templates/registration/login.html | 1 - games/templates/stats.html | 67 +++++------ games/templates/view_game.html | 135 +++++++++++++--------- 8 files changed, 193 insertions(+), 181 deletions(-) diff --git a/games/templates/add_purchase.html b/games/templates/add_purchase.html index 5a09d96..2fa0ec8 100644 --- a/games/templates/add_purchase.html +++ b/games/templates/add_purchase.html @@ -26,7 +26,9 @@ - Delete + Delete {% endif %} diff --git a/games/templates/base.html b/games/templates/base.html index bfc83de..edc6e7d 100644 --- a/games/templates/base.html +++ b/games/templates/base.html @@ -77,47 +77,47 @@ {% endif %} - - {% if session_count > 0 %} -
  • - Stats - +
  • +
  • + All Sessions +
  • +
  • + Log Out +
  • {% endif %} - - + {% endif %} + - -
    - {% block content %} - No content here. - {% endblock content %}
    - {% load version %} - {% version %} ({% version_date %}) + +
    + {% block content %} + No content here. + {% endblock content %}
    - {% block scripts %} - {% endblock scripts %} - - + {% load version %} + {% version %} ({% version_date %}) + + {% block scripts %} + {% endblock scripts %} + + diff --git a/games/templates/components/game_link.html b/games/templates/components/game_link.html index ae3fa4a..bcb7cfe 100644 --- a/games/templates/components/game_link.html +++ b/games/templates/components/game_link.html @@ -1,9 +1,10 @@ - + {% if children %} {{ children }} {% else %} {{ name }} {% endif %} - \ No newline at end of file + diff --git a/games/templates/components/popover.html b/games/templates/components/popover.html index 4bb218a..74d4ea0 100644 --- a/games/templates/components/popover.html +++ b/games/templates/components/popover.html @@ -1,9 +1,10 @@ - diff --git a/games/templates/list_sessions.html b/games/templates/list_sessions.html index 414261a..0f62f50 100644 --- a/games/templates/list_sessions.html +++ b/games/templates/list_sessions.html @@ -32,42 +32,43 @@ {% for session in dataset %} {% partialdef session-row inline=True %} - - - - - {{ session.purchase.edition.name }} - - - - - {{ session.timestamp_start | date:"d/m/Y H:i" }} - - - {% if not session.timestamp_end %} - {% url 'list_sessions_end_session' session.id as end_session_url %} - - Finish now? - - {% elif session.duration_manual %} - -- - {% else %} - {{ session.timestamp_end | date:"d/m/Y H:i" }} - {% endif %} - - {{ session.duration_formatted }} - - {% endpartialdef %} - {% endfor %} - - - {% else %} -
    No sessions found.
    - {% endif %} - + + + + + {{ session.purchase.edition.name }} + + + + + {{ session.timestamp_start | date:"d/m/Y H:i" }} + + + {% if not session.timestamp_end %} + {% url 'list_sessions_end_session' session.id as end_session_url %} + + Finish now? + + {% elif session.duration_manual %} + -- + {% else %} + {{ session.timestamp_end | date:"d/m/Y H:i" }} + {% endif %} + + {{ session.duration_formatted }} + + {% endpartialdef %} + {% endfor %} + + + {% else %} +
    No sessions found.
    + {% endif %} + {% endblock content %} diff --git a/games/templates/registration/login.html b/games/templates/registration/login.html index 8df4f0d..94bb70a 100644 --- a/games/templates/registration/login.html +++ b/games/templates/registration/login.html @@ -1,6 +1,5 @@ {% extends "base.html" %} {% load static %} - {% block title %} Login {% endblock title %} diff --git a/games/templates/stats.html b/games/templates/stats.html index 567e03b..f08ba1b 100644 --- a/games/templates/stats.html +++ b/games/templates/stats.html @@ -3,17 +3,15 @@ {{ title }} {% endblock title %} {% load static %} - {% partialdef purchase-name %} - {% if purchase.type != 'game' %} - {% #gamelink game_id=purchase.edition.game.id %} - {{ purchase.name }} ({{ purchase.edition.name }} {{ purchase.get_type_display }}) - {% /gamelink %} - {% else %} - {% gamelink game_id=purchase.edition.game.id name=purchase.edition.name %} - {% endif %} +{% if purchase.type != 'game' %} + {% #gamelink game_id=purchase.edition.game.id %} + {{ purchase.name }} ({{ purchase.edition.name }} {{ purchase.get_type_display }}) + {% /gamelink %} +{% else %} + {% gamelink game_id=purchase.edition.game.id name=purchase.edition.name %} +{% endif %} {% endpartialdef %} - {% block content %}
    @@ -66,11 +64,15 @@ Longest session - {{ longest_session_time }} ({% gamelink game_id=longest_session_game.id name=longest_session_game.name %}) + + {{ longest_session_time }} ({% gamelink game_id=longest_session_game.id name=longest_session_game.name %}) + Most sessions - {{ highest_session_count }} ({% gamelink game_id=highest_session_count_game.id name=highest_session_count_game.name %}) + + {{ highest_session_count }} ({% gamelink game_id=highest_session_count_game.id name=highest_session_count_game.name %}) + Highest session average @@ -80,15 +82,18 @@ First play - {% gamelink game_id=first_play_game.id name=first_play_game.name %} ({{ first_play_date }}) + + {% gamelink game_id=first_play_game.id name=first_play_game.name %} ({{ first_play_date }}) + Last play - {% gamelink game_id=last_play_game.id name=last_play_game.name %} ({{ last_play_date }}) + + {% gamelink game_id=last_play_game.id name=last_play_game.name %} ({{ last_play_date }}) + - {% if month_playtime %}

    Playtime per month

    @@ -102,7 +107,6 @@
    {% endif %} -

    Purchases

    @@ -118,9 +122,7 @@ - + @@ -149,9 +151,7 @@ {% for game in top_10_games_by_playtime %} - + {% endfor %} @@ -174,7 +174,6 @@ {% endfor %}
    Dropped - {{ dropped_count }} ({{ dropped_percentage }}%) - {{ dropped_count }} ({{ dropped_percentage }}%)
    Unfinished
    - {% gamelink game_id=game.id name=game.name %} - {% gamelink game_id=game.id name=game.name %} {{ game.formatted_playtime }}
    - {% if all_finished_this_year %}

    Finished

    @@ -187,16 +186,13 @@ {% for purchase in all_finished_this_year %} - + {% endfor %}
    - {% partial purchase-name %} - {% partial purchase-name %} {{ purchase.date_finished | date:"d/m/Y" }}
    {% endif %} - {% if this_year_finished_this_year %}

    Finished ({{ year }} games)

    @@ -209,16 +205,13 @@ {% for purchase in this_year_finished_this_year %} - + {% endfor %}
    - {% partial purchase-name %} - {% partial purchase-name %} {{ purchase.date_finished | date:"d/m/Y" }}
    {% endif %} - {% if purchased_this_year_finished_this_year %}

    Bought and Finished ({{ year }})

    @@ -231,16 +224,13 @@ {% for purchase in purchased_this_year_finished_this_year %} - + {% endfor %}
    - {% partial purchase-name %} - {% partial purchase-name %} {{ purchase.date_finished | date:"d/m/Y" }}
    {% endif %} - {% if purchased_unfinished %}

    Unfinished Purchases

    @@ -254,9 +244,7 @@ {% for purchase in purchased_unfinished %} - + @@ -264,7 +252,6 @@
    - {% partial purchase-name %} - {% partial purchase-name %} {{ purchase.price }} {{ purchase.date_purchased | date:"d/m/Y" }}
    {% endif %} - {% if all_purchased_this_year %}

    All Purchases

    @@ -278,9 +265,7 @@ {% for purchase in all_purchased_this_year %} - + diff --git a/games/templates/view_game.html b/games/templates/view_game.html index 7a3891e..bb566c0 100644 --- a/games/templates/view_game.html +++ b/games/templates/view_game.html @@ -11,62 +11,85 @@ {{ game.name }} {{ game.year_released }} {% #popover id="popover-year" %} - Original release year + Original release year {% /popover %}
    - + {{ hours_sum }} {% #popover id="popover-hours" %} - Total hours played + Total hours played {% /popover %} - - + + {{ session_count }} {% #popover id="popover-sessions" %} - Number of sessions + Number of sessions {% /popover %} - + {{ session_average_without_manual }} {% #popover id="popover-average" %} - Average playtime per session + Average playtime per session {% /popover %} - - + + {{ playrange }} {% #popover id="popover-playrange" %} - Earliest and latest dates played + Earliest and latest dates played {% /popover %}
    -

    Editions ({{ edition_count }}) and Purchases ({{ purchase_count }})

    @@ -110,55 +133,55 @@ ({{ session_count }}) {% if latest_session_id %} {% url 'view_game_start_session_from_session' latest_session_id as add_session_link %} - New + New {% endif %} and Notes ({{ sessions_with_notes_count }})
      {% for session in sessions %} {% partialdef session-info inline=True %} -
    • - {{ session.timestamp_start | date:"d/m/Y H:i" }}{% if session.timestamp_end %}-{{ session.timestamp_end | date:"H:i" }}{% endif %} - ({{ session.device.get_type_display | default:"Unknown" }}, {{ session.duration_formatted }}) - {% url 'edit_session' session.id as edit_url %} - {% include 'components/edit_button.html' with edit_url=edit_url %} - {% if not session.timestamp_end %} - {% url 'view_game_end_session' session.id as end_session_url %} - - - - - - - {% endif %} -
    • -
    • {{ session.note|markdown }}
    • - - {% endpartialdef %} - {% endfor %} -
    - - + {% endblock content %} -- 2.40.1 From 33012bc328980e9207d64bc9b87c341228961046 Mon Sep 17 00:00:00 2001 From: Lukas Kucharczyk Date: Thu, 8 Aug 2024 14:48:10 +0200 Subject: [PATCH 02/45] vscode: add extensions and settings --- .vscode/extensions.json | 5 ++++- .vscode/settings.json | 16 ++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 0d70e04..cedc387 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -4,5 +4,8 @@ "ms-python.python", "ms-python.vscode-pylance", "ms-python.debugpy", + "batisteo.vscode-django", + "bradlc.vscode-tailwindcss", + "EditorConfig.EditorConfig" ] -} \ No newline at end of file +} diff --git a/.vscode/settings.json b/.vscode/settings.json index fc1a347..c23f1c0 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -9,4 +9,20 @@ "editor.defaultFormatter": "ms-python.black-formatter", "editor.formatOnSave": true }, + "tailwind-fold.supportedLanguages": [ + "html", + "typescriptreact", + "javascriptreact", + "typescript", + "javascript", + "vue-html", + "vue", + "php", + "markdown", + "coffeescript", + "svelte", + "astro", + "erb", + "django-html" + ] } -- 2.40.1 From e33f23c18f8ac9e2bfa1c8a8189d99f8d7a1ca21 Mon Sep 17 00:00:00 2001 From: Lukas Kucharczyk Date: Thu, 8 Aug 2024 14:48:20 +0200 Subject: [PATCH 03/45] add .envrc --- .envrc | 1 + 1 file changed, 1 insertion(+) create mode 100644 .envrc diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..1d953f4 --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use nix -- 2.40.1 From 245b47b8b3fa9028469b62edaa83296672be514c Mon Sep 17 00:00:00 2001 From: Lukas Kucharczyk Date: Thu, 8 Aug 2024 14:48:58 +0200 Subject: [PATCH 04/45] improve shell.nix do not let poetry manage venvs no need to override python3 --- shell.nix | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/shell.nix b/shell.nix index 7423b46..065ef1b 100644 --- a/shell.nix +++ b/shell.nix @@ -5,10 +5,13 @@ pkgs.mkShell { buildInputs = with pkgs; [ nodejs - (poetry.override { python3 = python312; }) + python3 + poetry ]; shellHook = '' + python -m venv .venv + . .venv/bin/activate poetry install ''; } -- 2.40.1 From b6014a72e06f73e029dade426adbcc78717b217c Mon Sep 17 00:00:00 2001 From: Lukas Kucharczyk Date: Thu, 8 Aug 2024 14:49:09 +0200 Subject: [PATCH 05/45] .gitignore: add .direnv --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 983599a..d74ec92 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ db.sqlite3 dist/ .DS_Store .python-version +.direnv -- 2.40.1 From 6a5dc9b62cb177803cd733d5f06c3cc9c131ce1a Mon Sep 17 00:00:00 2001 From: Lukas Kucharczyk Date: Thu, 8 Aug 2024 15:02:39 +0200 Subject: [PATCH 06/45] even more formatting --- games/forms.py | 2 +- games/templates/list_sessions.html | 4 ++-- games/templates/view_game.html | 14 +++++++++----- games/templatetags/markdown_extras.py | 2 +- games/views.py | 15 ++------------- timetracker/urls.py | 1 + 6 files changed, 16 insertions(+), 22 deletions(-) diff --git a/games/forms.py b/games/forms.py index 1104d54..979d990 100644 --- a/games/forms.py +++ b/games/forms.py @@ -1,7 +1,7 @@ from django import forms from django.urls import reverse -from common.utils import safe_getattr +from common.utils import safe_getattr from games.models import Device, Edition, Game, Platform, Purchase, Session custom_date_widget = forms.DateInput(attrs={"type": "date"}) diff --git a/games/templates/list_sessions.html b/games/templates/list_sessions.html index 0f62f50..6aae78d 100644 --- a/games/templates/list_sessions.html +++ b/games/templates/list_sessions.html @@ -1,4 +1,4 @@ -{% extends 'base.html' %} +{% extends "base.html" %} {% load static %} {% block title %} {{ title }} @@ -15,7 +15,7 @@ hx-target=".responsive-table tbody" onClick="document.querySelector('#last-session-start').classList.add('invisible')" class="{% if last.timestamp_end == null %}invisible{% endif %}"> - {% include 'components/button_start.html' with text=last.purchase title="Start session of last played game" only %} + {% include "components/button_start.html" with text=last.purchase title="Start session of last played game" only %} {% endif %} diff --git a/games/templates/view_game.html b/games/templates/view_game.html index bb566c0..41c4b94 100644 --- a/games/templates/view_game.html +++ b/games/templates/view_game.html @@ -100,12 +100,16 @@ {% if edition.wikidata %} {% endif %} {% url 'edit_edition' edition.id as edit_url %} - {% include 'components/edit_button.html' with edit_url=edit_url %} + {% include "components/edit_button.html" with edit_url=edit_url %}
    - {% partial purchase-name %} - {% partial purchase-name %} {{ purchase.price }} {{ purchase.date_purchased | date:"d/m/Y" }}
    + + + {% for column in columns %}{% endfor %} + + + + {% for row in rows %} + {% table_row data=row %} + {% endfor %} + +
    {{ column }}
    +
    diff --git a/games/templates/components/table.html b/games/templates/components/table.html new file mode 100644 index 0000000..964da22 --- /dev/null +++ b/games/templates/components/table.html @@ -0,0 +1,12 @@ +
    + + + + {% for column in columns %}{% endfor %} + + + + {{ children }} + +
    {{ column }}
    +
    diff --git a/games/templates/components/table_row.html b/games/templates/components/table_row.html new file mode 100644 index 0000000..58072e9 --- /dev/null +++ b/games/templates/components/table_row.html @@ -0,0 +1,15 @@ +{% fragment as default_content %} +{% for td in data %} + {% if forloop.first %} + {{ td }} + {% else %} + {% #table_td %} + {{ td }} + {% /table_td %} + {% endif %} +{% endfor %} +{% endfragment %} + + {{ children|default:default_content }} + diff --git a/games/templates/components/table_td.html b/games/templates/components/table_td.html new file mode 100644 index 0000000..e4d7271 --- /dev/null +++ b/games/templates/components/table_td.html @@ -0,0 +1 @@ +{{ children }} diff --git a/games/templates/list_purchases.html b/games/templates/list_purchases.html new file mode 100644 index 0000000..8e3b2c5 --- /dev/null +++ b/games/templates/list_purchases.html @@ -0,0 +1,8 @@ +{% extends "base.html" %} +{% load static %} +{% block title %} + {{ title }} +{% endblock title %} +{% block content %} + {% simple_table columns=data.columns rows=data.rows %} +{% endblock content %} diff --git a/games/urls.py b/games/urls.py index 43936fa..7bbb60f 100644 --- a/games/urls.py +++ b/games/urls.py @@ -1,6 +1,6 @@ from django.urls import path -from games import views +from games import purchaseviews, views urlpatterns = [ path("", views.index, name="index"), @@ -25,6 +25,11 @@ urlpatterns = [ views.delete_purchase, name="delete_purchase", ), + path( + "purchase/list", + purchaseviews.list_purchases, + name="list_purchases", + ), path( "purchase/related-purchase-by-edition", views.related_purchase_by_edition, diff --git a/games/views.py b/games/views.py index 9ff9386..7a7cc0a 100644 --- a/games/views.py +++ b/games/views.py @@ -27,6 +27,9 @@ from .forms import ( ) from .models import Edition, Game, Platform, Purchase, Session +dateformat: str = "%d/%m/%Y" +datetimeformat: str = "%d/%m/%Y %H:%M" + def model_counts(request): return { -- 2.40.1 From a5ac10b20d9da28b7b321e535f7f8849f2d0a460 Mon Sep 17 00:00:00 2001 From: Lukas Kucharczyk Date: Thu, 8 Aug 2024 20:22:25 +0200 Subject: [PATCH 08/45] use model variables for foreign keys where possible --- games/models.py | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/games/models.py b/games/models.py index 3f5f3e5..3d08ee3 100644 --- a/games/models.py +++ b/games/models.py @@ -19,15 +19,24 @@ class Game(models.Model): return self.name +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 + + class Edition(models.Model): class Meta: unique_together = [["name", "platform", "year_released"]] - game = models.ForeignKey("Game", on_delete=models.CASCADE) + game = models.ForeignKey(Game, on_delete=models.CASCADE) name = models.CharField(max_length=255) sort_name = models.CharField(max_length=255, null=True, blank=True, default=None) platform = models.ForeignKey( - "Platform", on_delete=models.CASCADE, null=True, blank=True, default=None + Platform, on_delete=models.CASCADE, 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) @@ -83,9 +92,9 @@ class Purchase(models.Model): objects = PurchaseQueryset().as_manager() - edition = models.ForeignKey("Edition", on_delete=models.CASCADE) + edition = models.ForeignKey(Edition, on_delete=models.CASCADE) platform = models.ForeignKey( - "Platform", on_delete=models.CASCADE, default=None, null=True, blank=True + Platform, on_delete=models.CASCADE, default=None, null=True, blank=True ) date_purchased = models.DateField() date_refunded = models.DateField(blank=True, null=True) @@ -100,7 +109,7 @@ class Purchase(models.Model): type = models.CharField(max_length=255, choices=TYPES, default=GAME) name = models.CharField(max_length=255, default="", null=True, blank=True) related_purchase = models.ForeignKey( - "Purchase", + "self", on_delete=models.SET_NULL, default=None, null=True, @@ -135,15 +144,6 @@ class Purchase(models.Model): super().save(*args, **kwargs) -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 - - class SessionQuerySet(models.QuerySet): def total_duration_formatted(self): return format_duration(self.total_duration_unformatted()) @@ -172,7 +172,7 @@ class Session(models.Model): class Meta: get_latest_by = "timestamp_start" - purchase = models.ForeignKey("Purchase", on_delete=models.CASCADE) + purchase = models.ForeignKey(Purchase, on_delete=models.CASCADE) timestamp_start = models.DateTimeField() timestamp_end = models.DateTimeField(blank=True, null=True) duration_manual = models.DurationField(blank=True, null=True, default=timedelta(0)) -- 2.40.1 From 25deac6ea99f74a721de6c73b66d05ab8f0e0720 Mon Sep 17 00:00:00 2001 From: Lukas Kucharczyk Date: Thu, 8 Aug 2024 21:19:43 +0200 Subject: [PATCH 09/45] add more types --- common/time.py | 2 +- common/utils.py | 5 ++- games/models.py | 7 +++- games/views.py | 105 ++++++++++++++++++++++++++---------------------- 4 files changed, 66 insertions(+), 53 deletions(-) diff --git a/common/time.py b/common/time.py index a51f2ce..8e2d384 100644 --- a/common/time.py +++ b/common/time.py @@ -12,7 +12,7 @@ def _safe_timedelta(duration: timedelta | int | None): def format_duration( - duration: timedelta | int | None, format_string: str = "%H hours" + duration: timedelta | int | float | None, format_string: str = "%H hours" ) -> str: """ Format timedelta into the specified format_string. diff --git a/common/utils.py b/common/utils.py index ed82eb6..2b5632f 100644 --- a/common/utils.py +++ b/common/utils.py @@ -1,3 +1,6 @@ +from typing import Any + + def safe_division(numerator: int | float, denominator: int | float) -> int | float: """ Divides without triggering division by zero exception. @@ -9,7 +12,7 @@ def safe_division(numerator: int | float, denominator: int | float) -> int | flo return 0 -def safe_getattr(obj, attr_chain, default=None): +def safe_getattr(obj: object, attr_chain: str, default: Any | None = None) -> object: """ Safely get the nested attribute from an object. diff --git a/games/models.py b/games/models.py index 3d08ee3..a7f8aff 100644 --- a/games/models.py +++ b/games/models.py @@ -2,7 +2,7 @@ from datetime import timedelta from django.core.exceptions import ValidationError from django.db import models -from django.db.models import F, Manager, Sum +from django.db.models import F, Sum from django.utils import timezone from common.time import format_duration @@ -15,6 +15,9 @@ class Game(models.Model): wikidata = models.CharField(max_length=50, null=True, blank=True, default=None) created_at = models.DateTimeField(auto_now_add=True) + session_average: float | int | timedelta | None + session_count: int | None + def __str__(self): return self.name @@ -220,7 +223,7 @@ class Session(models.Model): def duration_sum(self) -> str: return Session.objects.all().total_duration_formatted() - def save(self, *args, **kwargs): + def save(self, *args, **kwargs) -> None: if self.timestamp_start != None and self.timestamp_end != None: self.duration_calculated = self.timestamp_end - self.timestamp_start else: diff --git a/games/views.py b/games/views.py index 7a7cc0a..1ee88c6 100644 --- a/games/views.py +++ b/games/views.py @@ -1,9 +1,10 @@ from datetime import datetime -from typing import Any, Callable +from typing import Any, Callable, TypedDict from django.contrib.auth.decorators import login_required from django.db.models import Avg, Count, ExpressionWrapper, F, Prefetch, Q, Sum, fields from django.db.models.functions import TruncDate, TruncMonth +from django.db.models.manager import BaseManager from django.http import ( HttpRequest, HttpResponse, @@ -25,13 +26,13 @@ from .forms import ( PurchaseForm, SessionForm, ) -from .models import Edition, Game, Platform, Purchase, Session +from .models import Edition, Game, Platform, Purchase, PurchaseQueryset, Session dateformat: str = "%d/%m/%Y" datetimeformat: str = "%d/%m/%Y %H:%M" -def model_counts(request): +def model_counts(request: HttpRequest) -> dict[str, bool]: return { "game_available": Game.objects.exists(), "edition_available": Edition.objects.exists(), @@ -41,15 +42,15 @@ def model_counts(request): } -def stats_dropdown_year_range(request): +def stats_dropdown_year_range(request: HttpRequest) -> dict[str, range]: result = {"stats_dropdown_year_range": range(timezone.now().year, 1999, -1)} return result @login_required -def add_session(request, purchase_id=None): +def add_session(request: HttpRequest, purchase_id: int) -> HttpResponse: context = {} - initial = {"timestamp_start": timezone.now()} + initial: dict[str, Any] = {"timestamp_start": timezone.now()} last = Session.objects.last() if last != None: @@ -97,9 +98,9 @@ def use_custom_redirect( @login_required @use_custom_redirect -def edit_session(request, session_id=None): +def edit_session(request: HttpRequest, session_id: int) -> HttpResponse: context = {} - session = Session.objects.get(id=session_id) + session = get_object_or_404(Session, id=session_id) form = SessionForm(request.POST or None, instance=session) if form.is_valid(): form.save() @@ -111,25 +112,25 @@ def edit_session(request, session_id=None): @login_required @use_custom_redirect -def edit_purchase(request, purchase_id=None): +def edit_purchase(request: HttpRequest, purchase_id: int) -> HttpResponse: context = {} - purchase = Purchase.objects.get(id=purchase_id) + purchase = get_object_or_404(Purchase, 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 - context["purchase_id"] = purchase_id + context["purchase_id"] = str(purchase_id) context["script_name"] = "add_purchase.js" return render(request, "add_purchase.html", context) @login_required @use_custom_redirect -def edit_game(request, game_id=None): +def edit_game(request: HttpRequest, game_id: int) -> HttpResponse: context = {} - purchase = Game.objects.get(id=game_id) + purchase = get_object_or_404(Game, id=game_id) form = GameForm(request.POST or None, instance=purchase) if form.is_valid(): form.save() @@ -140,23 +141,23 @@ def edit_game(request, game_id=None): @login_required -def delete_game(request, game_id=None): +def delete_game(request: HttpRequest, game_id: int) -> HttpResponse: game = get_object_or_404(Game, id=game_id) game.delete() return redirect("list_sessions") @login_required -def view_game(request, game_id=None): +def view_game(request: HttpRequest, game_id: int) -> HttpResponse: game = Game.objects.get(id=game_id) - nongame_related_purchases_prefetch = Prefetch( + nongame_related_purchases_prefetch: Prefetch[Purchase] = Prefetch( "related_purchases", queryset=Purchase.objects.exclude(type=Purchase.GAME).order_by( "date_purchased" ), to_attr="nongame_related_purchases", ) - game_purchases_prefetch = Prefetch( + game_purchases_prefetch: Prefetch[Purchase] = Prefetch( "purchase_set", queryset=Purchase.objects.filter(type=Purchase.GAME).prefetch_related( nongame_related_purchases_prefetch @@ -221,9 +222,9 @@ def view_game(request, game_id=None): @login_required @use_custom_redirect -def edit_platform(request, platform_id=None): +def edit_platform(request: HttpRequest, platform_id: int) -> HttpResponse: context = {} - purchase = Platform.objects.get(id=platform_id) + purchase = get_object_or_404(Purchase, id=platform_id) form = PlatformForm(request.POST or None, instance=purchase) if form.is_valid(): form.save() @@ -235,9 +236,9 @@ def edit_platform(request, platform_id=None): @login_required @use_custom_redirect -def edit_edition(request, edition_id=None): +def edit_edition(request: HttpRequest, edition_id: int) -> HttpResponse: context = {} - edition = Edition.objects.get(id=edition_id) + edition = get_object_or_404(Edition, id=edition_id) form = EditionForm(request.POST or None, instance=edition) if form.is_valid(): form.save() @@ -247,7 +248,7 @@ def edit_edition(request, edition_id=None): return render(request, "add.html", context) -def related_purchase_by_edition(request): +def related_purchase_by_edition(request: HttpRequest) -> HttpResponse: edition_id = request.GET.get("edition") if not edition_id: return HttpResponseBadRequest("Invalid edition_id") @@ -271,7 +272,9 @@ def clone_session_by_id(session_id: int) -> Session: @login_required @use_custom_redirect -def new_session_from_existing_session(request, session_id: int, template: str = ""): +def new_session_from_existing_session( + request: HttpRequest, session_id: int, template: str = "" +) -> HttpResponse: session = clone_session_by_id(session_id) if request.htmx: context = { @@ -284,7 +287,9 @@ def new_session_from_existing_session(request, session_id: int, template: str = @login_required @use_custom_redirect -def end_session(request, session_id: int, template: str = ""): +def end_session( + request: HttpRequest, session_id: int, template: str = "" +) -> HttpResponse: session = get_object_or_404(Session, id=session_id) session.timestamp_end = timezone.now() session.save() @@ -298,7 +303,7 @@ def end_session(request, session_id: int, template: str = ""): @login_required -def delete_session(request, session_id=None): +def delete_session(request: HttpRequest, session_id: int = 0) -> HttpResponse: session = get_object_or_404(Session, id=session_id) session.delete() return redirect("list_sessions") @@ -306,14 +311,14 @@ def delete_session(request, session_id=None): @login_required def list_sessions( - request, - filter="", - purchase_id="", - platform_id="", - game_id="", - edition_id="", + request: HttpRequest, + filter: str = "", + purchase_id: int = 0, + platform_id: int = 0, + game_id: int = 0, + edition_id: int = 0, ownership_type: str = "", -): +) -> HttpResponse: context = {} context["title"] = "Sessions" @@ -357,7 +362,7 @@ def list_sessions( @login_required -def stats_alltime(request): +def stats_alltime(request: HttpRequest) -> HttpResponse: year = "Alltime" this_year_sessions = Session.objects.all().select_related("purchase__edition") this_year_sessions_with_durations = this_year_sessions.annotate( @@ -425,7 +430,7 @@ def stats_alltime(request): * 100 ) - purchases_finished_this_year = Purchase.objects.finished() + purchases_finished_this_year: BaseManager[Purchase] = Purchase.objects.finished() purchases_finished_this_year_released_this_year = ( purchases_finished_this_year.all().order_by("date_finished") ) @@ -494,7 +499,7 @@ def stats_alltime(request): last_play_date = last_session.timestamp_start.strftime("%x") all_purchased_this_year_count = this_year_purchases_with_currency.count() - all_purchased_refunded_this_year_count = this_year_purchases_refunded.count() + all_purchased_refunded_this_year_count: int = this_year_purchases_refunded.count() this_year_purchases_dropped_count = this_year_purchases_dropped.count() this_year_purchases_dropped_percentage = int( @@ -569,7 +574,7 @@ def stats_alltime(request): @login_required -def stats(request, year: int = 0): +def stats(request: HttpRequest, year: int = 0) -> HttpResponse: selected_year = request.GET.get("year") if selected_year: return HttpResponseRedirect(reverse("stats_by_year", args=[selected_year])) @@ -710,6 +715,8 @@ def stats(request, year: int = 0): first_play_date = "N/A" last_play_date = "N/A" + first_play_game = None + last_play_game = None if this_year_sessions: first_session = this_year_sessions.earliest() first_play_game = first_session.purchase.edition.game @@ -817,15 +824,15 @@ def stats(request, year: int = 0): @login_required -def delete_purchase(request, purchase_id=None): +def delete_purchase(request: HttpRequest, purchase_id: int) -> HttpResponse: purchase = get_object_or_404(Purchase, id=purchase_id) purchase.delete() return redirect("list_sessions") @login_required -def add_purchase(request, edition_id=None): - context = {} +def add_purchase(request: HttpRequest, edition_id: int) -> HttpResponse: + context: dict[str, Any] = {} initial = {"date_purchased": timezone.now()} if request.method == "POST": @@ -860,8 +867,8 @@ def add_purchase(request, edition_id=None): @login_required -def add_game(request): - context = {} +def add_game(request: HttpRequest) -> HttpResponse: + context: dict[str, Any] = {} form = GameForm(request.POST or None) if form.is_valid(): game = form.save() @@ -879,8 +886,8 @@ def add_game(request): @login_required -def add_edition(request, game_id=None): - context = {} +def add_edition(request: HttpRequest, game_id: int) -> HttpResponse: + context: dict[str, Any] = {} if request.method == "POST": form = EditionForm(request.POST or None) if form.is_valid(): @@ -895,7 +902,7 @@ def add_edition(request, game_id=None): return redirect("index") else: if game_id: - game = Game.objects.get(id=game_id) + game = get_object_or_404(Game, id=game_id) form = EditionForm( initial={ "game": game, @@ -914,8 +921,8 @@ def add_edition(request, game_id=None): @login_required -def add_platform(request): - context = {} +def add_platform(request: HttpRequest) -> HttpResponse: + context: dict[str, Any] = {} form = PlatformForm(request.POST or None) if form.is_valid(): form.save() @@ -927,8 +934,8 @@ def add_platform(request): @login_required -def add_device(request): - context = {} +def add_device(request: HttpRequest) -> HttpResponse: + context: dict[str, Any] = {} form = DeviceForm(request.POST or None) if form.is_valid(): form.save() @@ -940,5 +947,5 @@ def add_device(request): @login_required -def index(request): +def index(request: HttpRequest) -> HttpResponse: return redirect("list_sessions_recent") -- 2.40.1 From 9ec6c958c8b8cad397669e8283881f29c00c4887 Mon Sep 17 00:00:00 2001 From: Lukas Kucharczyk Date: Thu, 8 Aug 2024 21:20:08 +0200 Subject: [PATCH 10/45] remove unnecessary styles --- games/templates/components/table_row.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/games/templates/components/table_row.html b/games/templates/components/table_row.html index 58072e9..049c402 100644 --- a/games/templates/components/table_row.html +++ b/games/templates/components/table_row.html @@ -10,6 +10,6 @@ {% endif %} {% endfor %} {% endfragment %} - + {{ children|default:default_content }} -- 2.40.1 From b54bcdd9e967f2f3f5e614a9194975dddec23bf6 Mon Sep 17 00:00:00 2001 From: Lukas Kucharczyk Date: Thu, 8 Aug 2024 21:20:17 +0200 Subject: [PATCH 11/45] remove cruft --- games/static/base.css | 8 -------- 1 file changed, 8 deletions(-) diff --git a/games/static/base.css b/games/static/base.css index ee35cb5..c4faa99 100644 --- a/games/static/base.css +++ b/games/static/base.css @@ -2287,14 +2287,6 @@ textarea:disabled:is(.dark *) { margin-right: 0.25rem; } -/* th { - @apply text-right; -} */ - -/* th label { - @apply mr-4; -} */ - .basic-button-container { display: flex; justify-content: center; -- 2.40.1 From 1c28950b530f5b1c27d37a331e5e0263445be8cb Mon Sep 17 00:00:00 2001 From: Lukas Kucharczyk Date: Thu, 8 Aug 2024 22:54:15 +0200 Subject: [PATCH 12/45] add pagination --- games/purchaseviews.py | 10 ++- games/static/base.css | 71 ++++++++++++-------- games/templates/components/simple_table.html | 36 +++++++++- games/templates/list_purchases.html | 2 +- 4 files changed, 88 insertions(+), 31 deletions(-) diff --git a/games/purchaseviews.py b/games/purchaseviews.py index c740786..0f68a5b 100644 --- a/games/purchaseviews.py +++ b/games/purchaseviews.py @@ -1,6 +1,7 @@ from typing import Any from django.contrib.auth.decorators import login_required +from django.core.paginator import Paginator from django.db.models.manager import BaseManager from django.http import HttpRequest, HttpResponse from django.shortcuts import render @@ -14,9 +15,16 @@ from games.views import dateformat @login_required def list_purchases(request: HttpRequest) -> HttpResponse: context: dict[Any, Any] = {} - purchases: BaseManager[Purchase] = Purchase.objects.all()[0:10] + paginator = Paginator(Purchase.objects.order_by("created_at"), 10) + page_number = request.GET.get("page", 1) + page_obj = paginator.get_page(page_number) + purchases = page_obj.object_list context = { "title": "Manage purchases", + "page_obj": page_obj, + "elided_page_range": page_obj.paginator.get_elided_page_range( + page_number, on_each_side=1, on_ends=1 + ), "data": { "columns": [ "Name", diff --git a/games/static/base.css b/games/static/base.css index c4faa99..75bea80 100644 --- a/games/static/base.css +++ b/games/static/base.css @@ -1370,6 +1370,10 @@ input:checked + .toggle-bg { margin-bottom: 0.75rem; } +.mb-4 { + margin-bottom: 1rem; +} + .mb-8 { margin-bottom: 2rem; } @@ -1382,6 +1386,10 @@ input:checked + .toggle-bg { margin-right: 1rem; } +.ms-0 { + margin-inline-start: 0px; +} + .mt-2 { margin-top: 0.5rem; } @@ -1451,6 +1459,10 @@ input:checked + .toggle-bg { height: 1.5rem; } +.h-8 { + height: 2rem; +} + .h-9 { height: 2.25rem; } @@ -1639,6 +1651,12 @@ input:checked + .toggle-bg { gap: 1.25rem; } +.-space-x-px > :not([hidden]) ~ :not([hidden]) { + --tw-space-x-reverse: 0; + margin-right: calc(-1px * var(--tw-space-x-reverse)); + margin-left: calc(-1px * calc(1 - var(--tw-space-x-reverse))); +} + .space-x-1 > :not([hidden]) ~ :not([hidden]) { --tw-space-x-reverse: 0; margin-right: calc(0.25rem * var(--tw-space-x-reverse)); @@ -1885,6 +1903,10 @@ input:checked + .toggle-bg { padding-top: 0.5rem; } +.pt-4 { + padding-top: 1rem; +} + .pt-8 { padding-top: 2rem; } @@ -1966,6 +1988,10 @@ input:checked + .toggle-bg { font-weight: 500; } +.font-normal { + font-weight: 400; +} + .font-semibold { font-weight: 600; } @@ -1982,6 +2008,10 @@ input:checked + .toggle-bg { line-height: 2.25rem; } +.leading-tight { + line-height: 1.25; +} + .text-blue-600 { --tw-text-opacity: 1; color: rgb(28 100 242 / var(--tw-text-opacity)); @@ -2474,6 +2504,11 @@ textarea:disabled:is(.dark *) { color: rgb(75 85 99 / var(--tw-text-opacity)); } +.hover\:text-gray-700:hover { + --tw-text-opacity: 1; + color: rgb(55 65 81 / var(--tw-text-opacity)); +} + .hover\:text-gray-900:hover { --tw-text-opacity: 1; color: rgb(17 24 39 / var(--tw-text-opacity)); @@ -2845,10 +2880,18 @@ textarea:disabled:is(.dark *) { } @media (min-width: 768px) { + .md\:mb-0 { + margin-bottom: 0px; + } + .md\:block { display: block; } + .md\:inline { + display: inline; + } + .md\:w-auto { width: auto; } @@ -2904,31 +2947,3 @@ textarea:disabled:is(.dark *) { border-start-end-radius: 0.5rem; border-end-end-radius: 0.5rem; } - -.\[\&_td\:first-of-type\]\:whitespace-nowrap td:first-of-type { - white-space: nowrap; -} - -.\[\&_td\:first-of-type\]\:px-6 td:first-of-type { - padding-left: 1.5rem; - padding-right: 1.5rem; -} - -.\[\&_td\:first-of-type\]\:py-4 td:first-of-type { - padding-top: 1rem; - padding-bottom: 1rem; -} - -.\[\&_td\:first-of-type\]\:font-medium td:first-of-type { - font-weight: 500; -} - -.\[\&_td\:first-of-type\]\:text-gray-900 td:first-of-type { - --tw-text-opacity: 1; - color: rgb(17 24 39 / var(--tw-text-opacity)); -} - -.\[\&_td\:first-of-type\]\:dark\:text-white:is(.dark *) td:first-of-type { - --tw-text-opacity: 1; - color: rgb(255 255 255 / var(--tw-text-opacity)); -} diff --git a/games/templates/components/simple_table.html b/games/templates/components/simple_table.html index a13a93a..c412e79 100644 --- a/games/templates/components/simple_table.html +++ b/games/templates/components/simple_table.html @@ -1,4 +1,5 @@ -
    +
    @@ -11,4 +12,37 @@ {% endfor %}
    +
    diff --git a/games/templates/list_purchases.html b/games/templates/list_purchases.html index 8e3b2c5..67cdc3c 100644 --- a/games/templates/list_purchases.html +++ b/games/templates/list_purchases.html @@ -4,5 +4,5 @@ {{ title }} {% endblock title %} {% block content %} - {% simple_table columns=data.columns rows=data.rows %} + {% simple_table columns=data.columns rows=data.rows page_obj=page_obj elided_page_range=elided_page_range %} {% endblock content %} -- 2.40.1 From d84b67c46023147d26e96443ab10cfbc1c34580b Mon Sep 17 00:00:00 2001 From: Lukas Kucharczyk Date: Fri, 9 Aug 2024 11:47:10 +0200 Subject: [PATCH 13/45] improve pagination --- common/input.css | 6 +++ games/purchaseviews.py | 2 +- games/static/base.css | 49 ++++++++++++++++++++ games/templates/base.html | 2 +- games/templates/components/simple_table.html | 10 ++-- games/templates/components/table_row.html | 6 ++- games/templates/components/table_td.html | 2 +- games/templates/list_purchases.html | 4 +- 8 files changed, 70 insertions(+), 11 deletions(-) diff --git a/common/input.css b/common/input.css index 41edc14..75e4442 100644 --- a/common/input.css +++ b/common/input.css @@ -70,9 +70,15 @@ form label { } @layer utilities { + .min-w-20char { + min-width: 20ch; + } .max-w-20char { max-width: 20ch; } + .min-w-30char { + min-width: 30ch; + } .max-w-30char { max-width: 30ch; } diff --git a/games/purchaseviews.py b/games/purchaseviews.py index 0f68a5b..a268d3d 100644 --- a/games/purchaseviews.py +++ b/games/purchaseviews.py @@ -15,7 +15,7 @@ from games.views import dateformat @login_required def list_purchases(request: HttpRequest) -> HttpResponse: context: dict[Any, Any] = {} - paginator = Paginator(Purchase.objects.order_by("created_at"), 10) + paginator = Paginator(Purchase.objects.order_by("-date_purchased"), 10) page_number = request.GET.get("page", 1) page_obj = paginator.get_page(page_number) purchases = page_obj.object_list diff --git a/games/static/base.css b/games/static/base.css index 75bea80..fef6550 100644 --- a/games/static/base.css +++ b/games/static/base.css @@ -1523,6 +1523,18 @@ input:checked + .toggle-bg { max-width: 20rem; } +.max-w-screen-sm { + max-width: 640px; +} + +.max-w-screen-xl { + max-width: 1280px; +} + +.max-w-screen-2xl { + max-width: 1536px; +} + .flex-1 { flex: 1 1 0%; } @@ -1603,6 +1615,10 @@ input:checked + .toggle-bg { grid-template-columns: repeat(7, minmax(0, 1fr)); } +.flex-row { + flex-direction: row; +} + .flex-col { flex-direction: column; } @@ -1687,6 +1703,14 @@ input:checked + .toggle-bg { white-space: nowrap; } +.overflow-ellipsis { + text-overflow: ellipsis; +} + +.text-ellipsis { + text-overflow: ellipsis; +} + .whitespace-nowrap { white-space: nowrap; } @@ -1833,6 +1857,10 @@ input:checked + .toggle-bg { padding: 1rem; } +.p-6 { + padding: 1.5rem; +} + .px-2 { padding-left: 0.5rem; padding-right: 0.5rem; @@ -1911,6 +1939,10 @@ input:checked + .toggle-bg { padding-top: 2rem; } +.pb-4 { + padding-bottom: 1rem; +} + .text-left { text-align: left; } @@ -2160,6 +2192,10 @@ input:checked + .toggle-bg { max-width: 20ch; } +.max-w-30char { + max-width: 30ch; +} + .\[a-zA-Z\:\\-\] { a-z-a--z: \-; } @@ -2419,6 +2455,14 @@ textarea:disabled:is(.dark *) { } } */ +.last\:border-b:last-child { + border-bottom-width: 1px; +} + +.last\:border-b-0:last-child { + border-bottom-width: 0px; +} + .odd\:bg-white:nth-child(odd) { --tw-bg-opacity: 1; background-color: rgb(255 255 255 / var(--tw-bg-opacity)); @@ -2752,6 +2796,11 @@ textarea:disabled:is(.dark *) { color: rgb(255 255 255 / var(--tw-text-opacity)); } +.dark\:text-gray-600:is(.dark *) { + --tw-text-opacity: 1; + color: rgb(75 85 99 / var(--tw-text-opacity)); +} + .odd\:dark\:bg-gray-900:is(.dark *):nth-child(odd) { --tw-bg-opacity: 1; background-color: rgb(17 24 39 / var(--tw-bg-opacity)); diff --git a/games/templates/base.html b/games/templates/base.html index edc6e7d..ee17f8d 100644 --- a/games/templates/base.html +++ b/games/templates/base.html @@ -109,7 +109,7 @@
    -
    +
    {% block content %} No content here. {% endblock content %} diff --git a/games/templates/components/simple_table.html b/games/templates/components/simple_table.html index c412e79..8dd49df 100644 --- a/games/templates/components/simple_table.html +++ b/games/templates/components/simple_table.html @@ -12,9 +12,9 @@ {% endfor %} -
    {% block scripts %} {% endblock scripts %} -- 2.40.1 From c15eaca205e812d22ea0d179fd7aaa10d6b22e65 Mon Sep 17 00:00:00 2001 From: Lukas Kucharczyk Date: Fri, 9 Aug 2024 12:42:54 +0200 Subject: [PATCH 17/45] only overflow table, not paginator, improve styling --- games/static/base.css | 39 ++++++++++++++++++++ games/templates/components/simple_table.html | 37 ++++++++++--------- games/templates/list_purchases.html | 2 +- 3 files changed, 59 insertions(+), 19 deletions(-) diff --git a/games/static/base.css b/games/static/base.css index 94ad2d7..a4d9b46 100644 --- a/games/static/base.css +++ b/games/static/base.css @@ -1829,6 +1829,16 @@ input:checked + .toggle-bg { background-color: rgb(255 255 255 / 0.5); } +.bg-gray-300 { + --tw-bg-opacity: 1; + background-color: rgb(209 213 219 / var(--tw-bg-opacity)); +} + +.bg-gray-400 { + --tw-bg-opacity: 1; + background-color: rgb(156 163 175 / var(--tw-bg-opacity)); +} + .p-1 { padding: 0.25rem; } @@ -2067,6 +2077,16 @@ input:checked + .toggle-bg { color: rgb(250 202 21 / var(--tw-text-opacity)); } +.text-gray-300 { + --tw-text-opacity: 1; + color: rgb(209 213 219 / var(--tw-text-opacity)); +} + +.text-gray-100 { + --tw-text-opacity: 1; + color: rgb(243 244 246 / var(--tw-text-opacity)); +} + .underline { text-decoration-line: underline; } @@ -2761,6 +2781,11 @@ textarea:disabled:is(.dark *) { color: rgb(255 255 255 / var(--tw-text-opacity)); } +.dark\:text-gray-200:is(.dark *) { + --tw-text-opacity: 1; + color: rgb(229 231 235 / var(--tw-text-opacity)); +} + .odd\:dark\:bg-gray-900:is(.dark *):nth-child(odd) { --tw-bg-opacity: 1; background-color: rgb(17 24 39 / var(--tw-bg-opacity)); @@ -2863,6 +2888,10 @@ textarea:disabled:is(.dark *) { max-width: 36rem; } + .sm\:max-w-screen-sm { + max-width: 640px; + } + .sm\:rounded-lg { border-radius: 0.5rem; } @@ -2910,6 +2939,10 @@ textarea:disabled:is(.dark *) { width: auto; } + .md\:max-w-screen-md { + max-width: 768px; + } + .md\:flex-row { flex-direction: row; } @@ -2939,6 +2972,12 @@ textarea:disabled:is(.dark *) { } } +@media (min-width: 1536px) { + .\32xl\:max-w-screen-2xl { + max-width: 1536px; + } +} + .rtl\:rotate-180:where([dir="rtl"], [dir="rtl"] *) { --tw-rotate: 180deg; transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); diff --git a/games/templates/components/simple_table.html b/games/templates/components/simple_table.html index e3379cf..033e890 100644 --- a/games/templates/components/simple_table.html +++ b/games/templates/components/simple_table.html @@ -1,19 +1,20 @@ -
    - - - - {% for column in columns %}{% endfor %} - - - - {% for row in rows %} - {% table_row data=row %} - {% endfor %} - -
    {{ column }}
    +
    +
    + + + + {% for column in columns %}{% endfor %} + + + + {% for row in rows %} + {% table_row data=row %} + {% endfor %} + +
    {{ column }}
    +
    {% if page_obj %} -
    diff --git a/games/templates/components/simple_table.html b/games/templates/cotton/simple_table.html similarity index 91% rename from games/templates/components/simple_table.html rename to games/templates/cotton/simple_table.html index ab68ac2..62c413f 100644 --- a/games/templates/components/simple_table.html +++ b/games/templates/cotton/simple_table.html @@ -1,3 +1,4 @@ +{% load param_utils %} diff --git a/games/templates/cotton/table_row.html b/games/templates/cotton/table_row.html new file mode 100644 index 0000000..4ca4622 --- /dev/null +++ b/games/templates/cotton/table_row.html @@ -0,0 +1,16 @@ + + {% if slot %} + {{ slot }} + {% else %} + {% for td in data %} + {% if forloop.first %} + {{ td }} + {% else %} + + {{ td }} + + {% endif %} + {% endfor %} + {% endif %} + diff --git a/games/templates/cotton/table_td.html b/games/templates/cotton/table_td.html new file mode 100644 index 0000000..47d2fca --- /dev/null +++ b/games/templates/cotton/table_td.html @@ -0,0 +1 @@ +{{ slot }} diff --git a/games/templates/list_purchases.html b/games/templates/list_purchases.html index 443e029..ac553d9 100644 --- a/games/templates/list_purchases.html +++ b/games/templates/list_purchases.html @@ -5,6 +5,6 @@ {% endblock title %} {% block content %}
    - {% simple_table columns=data.columns rows=data.rows page_obj=page_obj elided_page_range=elided_page_range %} +
    {% endblock content %} diff --git a/games/templates/stats.html b/games/templates/stats.html index f08ba1b..676b1c9 100644 --- a/games/templates/stats.html +++ b/games/templates/stats.html @@ -5,11 +5,11 @@ {% load static %} {% partialdef purchase-name %} {% if purchase.type != 'game' %} - {% #gamelink game_id=purchase.edition.game.id %} + {{ purchase.name }} ({{ purchase.edition.name }} {{ purchase.get_type_display }}) - {% /gamelink %} + {% else %} - {% gamelink game_id=purchase.edition.game.id name=purchase.edition.name %} + {% endif %} {% endpartialdef %} {% block content %} @@ -65,31 +65,31 @@ Longest session - {{ longest_session_time }} ({% gamelink game_id=longest_session_game.id name=longest_session_game.name %}) + {{ longest_session_time }} () Most sessions - {{ highest_session_count }} ({% gamelink game_id=highest_session_count_game.id name=highest_session_count_game.name %}) + {{ highest_session_count }} () Highest session average - {{ highest_session_average }} ({% gamelink game_id=highest_session_average_game.id name=highest_session_average_game.name %}) + {{ highest_session_average }} () First play - {% gamelink game_id=first_play_game.id name=first_play_game.name %} ({{ first_play_date }}) + ({{ first_play_date }}) Last play - {% gamelink game_id=last_play_game.id name=last_play_game.name %} ({{ last_play_date }}) + ({{ last_play_date }}) @@ -151,7 +151,9 @@ {% for game in top_10_games_by_playtime %} - {% gamelink game_id=game.id name=game.name %} + + + {{ game.formatted_playtime }} {% endfor %} diff --git a/games/templates/view_game.html b/games/templates/view_game.html index bc2d5a8..c0f647d 100644 --- a/games/templates/view_game.html +++ b/games/templates/view_game.html @@ -10,9 +10,9 @@
    {{ game.name }} {{ game.year_released }} - {% #popover id="popover-year" %} + Original release year - {% /popover %} +
    @@ -26,9 +26,9 @@ {{ hours_sum }} - {% #popover id="popover-hours" %} + Total hours played - {% /popover %} + @@ -41,9 +41,9 @@ {{ session_count }} - {% #popover id="popover-sessions" %} + Number of sessions - {% /popover %} + {{ session_average_without_manual }} - {% #popover id="popover-average" %} + Average playtime per session - {% /popover %} + @@ -70,9 +70,9 @@ {{ playrange }} - {% #popover id="popover-playrange" %} + Earliest and latest dates played - {% /popover %} +
    - {% #h1 badge=edition_count %}Editions{% /h1 %} -
    {% simple_table rows=edition_data.rows columns=edition_data.columns %}
    + Editions
    - {% #h1 badge=purchase_count %}Purchases{% /h1 %} - {% simple_table rows=purchase_data.rows columns=purchase_data.columns %} +
    - {% #h1 badge=session_count %}Sessions{% /h1 %} - {% simple_table rows=session_data.rows columns=session_data.columns page_obj=session_page_obj elided_page_range=session_elided_page_range %} + Purchases + +
    +
    + Sessions +