From b22e185d47ff5c4eab748ae8107ff14f49435c76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Kucharczyk?= Date: Tue, 4 Feb 2025 20:09:05 +0100 Subject: [PATCH] Add status, mastered to Game --- games/forms.py | 28 +++++++- .../0005_game_mastered_game_status.py | 38 ++++++++++ games/models.py | 25 +++++++ games/static/base.css | 70 +++++++++++++++++++ games/templates/cotton/gamestatus.html | 16 +++++ games/templates/view_game.html | 12 ++++ games/views/game.py | 5 ++ 7 files changed, 193 insertions(+), 1 deletion(-) create mode 100644 games/migrations/0005_game_mastered_game_status.py create mode 100644 games/templates/cotton/gamestatus.html diff --git a/games/forms.py b/games/forms.py index a9c50b4..046b516 100644 --- a/games/forms.py +++ b/games/forms.py @@ -24,6 +24,12 @@ class SessionForm(forms.ModelForm): device = forms.ModelChoiceField(queryset=Device.objects.order_by("name")) + mark_as_played = forms.BooleanField( + required=False, + initial={"mark_as_played": True}, + label="Set game status to Played if Unplayed", + ) + class Meta: widgets = { "timestamp_start": custom_datetime_widget, @@ -38,8 +44,21 @@ class SessionForm(forms.ModelForm): "emulated", "device", "note", + "mark_as_played", ] + def save(self, commit=True): + session = super().save(commit=False) + if self.cleaned_data.get("mark_as_played"): + game_instance = session.game + if game_instance.status == "u": + game_instance.status = "p" + if commit: + game_instance.save() + if commit: + session.save() + return session + class IncludePlatformSelect(forms.SelectMultiple): def create_option(self, name, value, *args, **kwargs): @@ -143,7 +162,14 @@ class GameForm(forms.ModelForm): class Meta: model = Game - fields = ["name", "sort_name", "platform", "year_released", "wikidata"] + fields = [ + "name", + "sort_name", + "platform", + "year_released", + "status", + "wikidata", + ] widgets = {"name": autofocus_input_widget} diff --git a/games/migrations/0005_game_mastered_game_status.py b/games/migrations/0005_game_mastered_game_status.py new file mode 100644 index 0000000..0967004 --- /dev/null +++ b/games/migrations/0005_game_mastered_game_status.py @@ -0,0 +1,38 @@ +# Generated by Django 5.1.5 on 2025-02-01 19:18 + +from django.db import migrations, models + + +def set_finished_status(apps, schema_editor): + Game = apps.get_model("games", "Game") + Game.objects.filter(purchases__date_finished__isnull=False).update(status="f") + + +class Migration(migrations.Migration): + dependencies = [ + ("games", "0004_purchase_num_purchases"), + ] + + operations = [ + migrations.AddField( + model_name="game", + name="mastered", + field=models.BooleanField(default=False), + ), + migrations.AddField( + model_name="game", + name="status", + field=models.CharField( + choices=[ + ("u", "Unplayed"), + ("p", "Played"), + ("f", "Finished"), + ("r", "Retired"), + ("a", "Abandoned"), + ], + default="u", + max_length=1, + ), + ), + migrations.RunPython(set_finished_status), + ] diff --git a/games/models.py b/games/models.py index eba5848..6a84b78 100644 --- a/games/models.py +++ b/games/models.py @@ -23,6 +23,31 @@ class Game(models.Model): created_at = models.DateTimeField(auto_now_add=True) + class Status(models.TextChoices): + UNPLAYED = ( + "u", + "Unplayed", + ) + PLAYED = ( + "p", + "Played", + ) + FINISHED = ( + "f", + "Finished", + ) + RETIRED = ( + "r", + "Retired", + ) + ABANDONED = ( + "a", + "Abandoned", + ) + + status = models.CharField(max_length=1, choices=Status, default=Status.UNPLAYED) + mastered = models.BooleanField(default=False) + session_average: float | int | timedelta | None session_count: int | None diff --git a/games/static/base.css b/games/static/base.css index b27e1bf..f8c3d30 100644 --- a/games/static/base.css +++ b/games/static/base.css @@ -1299,6 +1299,10 @@ input:checked + .toggle-bg { bottom: 0px; } +.-left-3 { + left: -0.75rem; +} + .bottom-0 { bottom: 0px; } @@ -1335,6 +1339,10 @@ input:checked + .toggle-bg { top: 0px; } +.top-2 { + top: 0.5rem; +} + .top-3 { top: 0.75rem; } @@ -1415,6 +1423,10 @@ input:checked + .toggle-bg { margin-inline-end: 0.5rem; } +.ml-3 { + margin-left: 0.75rem; +} + .mr-4 { margin-right: 1rem; } @@ -1488,6 +1500,10 @@ input:checked + .toggle-bg { height: 3rem; } +.h-2 { + height: 0.5rem; +} + .h-2\.5 { height: 0.625rem; } @@ -1524,6 +1540,10 @@ input:checked + .toggle-bg { width: 2.5rem; } +.w-2 { + width: 0.5rem; +} + .w-2\.5 { width: 0.625rem; } @@ -1668,6 +1688,10 @@ input:checked + .toggle-bg { grid-template-columns: repeat(7, minmax(0, 1fr)); } +.flex-row { + flex-direction: row; +} + .flex-col { flex-direction: column; } @@ -1716,6 +1740,14 @@ input:checked + .toggle-bg { gap: 1.25rem; } +.gap-3 { + gap: 0.75rem; +} + +.gap-6 { + gap: 1.5rem; +} + .-space-x-px > :not([hidden]) ~ :not([hidden]) { --tw-space-x-reverse: 0; margin-right: calc(-1px * var(--tw-space-x-reverse)); @@ -1787,6 +1819,10 @@ input:checked + .toggle-bg { border-radius: 0.125rem; } +.rounded-xl { + border-radius: 0.75rem; +} + .rounded-e-lg { border-start-end-radius: 0.5rem; border-end-end-radius: 0.5rem; @@ -1880,6 +1916,11 @@ input:checked + .toggle-bg { background-color: rgb(249 250 251 / var(--tw-bg-opacity)); } +.bg-gray-500 { + --tw-bg-opacity: 1; + background-color: rgb(107 114 128 / var(--tw-bg-opacity)); +} + .bg-gray-800 { --tw-bg-opacity: 1; background-color: rgb(31 41 55 / var(--tw-bg-opacity)); @@ -1889,6 +1930,11 @@ input:checked + .toggle-bg { background-color: rgb(17 24 39 / 0.5); } +.bg-green-500 { + --tw-bg-opacity: 1; + background-color: rgb(14 159 110 / var(--tw-bg-opacity)); +} + .bg-green-600 { --tw-bg-opacity: 1; background-color: rgb(5 122 85 / var(--tw-bg-opacity)); @@ -1899,6 +1945,21 @@ input:checked + .toggle-bg { background-color: rgb(4 108 78 / var(--tw-bg-opacity)); } +.bg-orange-400 { + --tw-bg-opacity: 1; + background-color: rgb(255 138 76 / var(--tw-bg-opacity)); +} + +.bg-purple-500 { + --tw-bg-opacity: 1; + background-color: rgb(144 97 249 / var(--tw-bg-opacity)); +} + +.bg-red-500 { + --tw-bg-opacity: 1; + background-color: rgb(240 82 82 / var(--tw-bg-opacity)); +} + .bg-red-700 { --tw-bg-opacity: 1; background-color: rgb(200 30 30 / var(--tw-bg-opacity)); @@ -2030,6 +2091,10 @@ input:checked + .toggle-bg { vertical-align: top; } +.align-middle { + vertical-align: middle; +} + .font-mono { font-family: IBM Plex Mono, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; } @@ -2192,6 +2257,11 @@ input:checked + .toggle-bg { color: rgb(250 202 21 / var(--tw-text-opacity)); } +.text-slate-400 { + --tw-text-opacity: 1; + color: rgb(148 163 184 / var(--tw-text-opacity)); +} + .underline { text-decoration-line: underline; } diff --git a/games/templates/cotton/gamestatus.html b/games/templates/cotton/gamestatus.html new file mode 100644 index 0000000..0f48b0a --- /dev/null +++ b/games/templates/cotton/gamestatus.html @@ -0,0 +1,16 @@ +
+   + {{ slot }} +
\ No newline at end of file diff --git a/games/templates/view_game.html b/games/templates/view_game.html index 2b3859f..8d4b662 100644 --- a/games/templates/view_game.html +++ b/games/templates/view_game.html @@ -52,6 +52,18 @@ {{ playrange }} +
+
+ Status + + {{ game.get_status_display }} + +
+
+ Platform + {{ game.platform }} +
+