diff --git a/common/utils.py b/common/utils.py index 2b5632f..098dc3a 100644 --- a/common/utils.py +++ b/common/utils.py @@ -1,5 +1,31 @@ +from random import choices +from string import ascii_lowercase from typing import Any +from django.template.loader import render_to_string +from django.utils.safestring import mark_safe + + +def Popover( + wrapped_content: str, + popover_content: str = "", +) -> str: + id = randomid() + if popover_content == "": + popover_content = wrapped_content + content = f"{wrapped_content}" + result = mark_safe( + str(content) + + render_to_string( + "components/popover.html", + { + "id": id, + "children": popover_content, + }, + ) + ) + return result + def safe_division(numerator: int | float, denominator: int | float) -> int | float: """ @@ -31,3 +57,24 @@ def safe_getattr(obj: object, attr_chain: str, default: Any | None = None) -> ob except AttributeError: return default return obj + + +def truncate(input_string: str, length: int = 30, ellipsis: str = "…") -> str: + return ( + (f"{input_string[:length-len(ellipsis)]}{ellipsis}") + if len(input_string) > 30 + else input_string + ) + + +def truncate_with_popover(input_string: str) -> str: + if (truncated := truncate(input_string)) != input_string: + print(f"Not the same after: {truncated=}") + return Popover(wrapped_content=truncated, popover_content=input_string) + else: + print("Strings are the same!") + return input_string + + +def randomid(seed: str = "", length: int = 10) -> str: + return seed + "".join(choices(ascii_lowercase, k=length)) diff --git a/games/editionviews.py b/games/editionviews.py new file mode 100644 index 0000000..ad1769f --- /dev/null +++ b/games/editionviews.py @@ -0,0 +1,109 @@ +from typing import Any + +from django.contrib.auth.decorators import login_required +from django.core.paginator import Paginator +from django.http import HttpRequest, HttpResponse +from django.shortcuts import get_object_or_404, redirect, render +from django.template.loader import render_to_string +from django.urls import reverse + +from common.utils import truncate_with_popover +from games.forms import EditionForm +from games.models import Edition +from games.views import dateformat + + +@login_required +def list_editions(request: HttpRequest) -> HttpResponse: + context: dict[Any, Any] = {} + page_number = request.GET.get("page", 1) + limit = request.GET.get("limit", 10) + editions = Edition.objects.order_by("-created_at") + page_obj = None + if int(limit) != 0: + paginator = Paginator(editions, limit) + page_obj = paginator.get_page(page_number) + editions = page_obj.object_list + + context = { + "title": "Manage editions", + "page_obj": page_obj or None, + "elided_page_range": ( + page_obj.paginator.get_elided_page_range( + page_number, on_each_side=1, on_ends=1 + ) + if page_obj + else None + ), + "data": { + "columns": [ + "Game", + "Name", + "Sort Name", + "Platform", + "Year", + "Wikidata", + "Created", + "Actions", + ], + "rows": [ + [ + truncate_with_popover(edition.game.name), + truncate_with_popover( + edition.name + if edition.game.name != edition.name + else "(identical)" + ), + truncate_with_popover( + edition.sort_name + if edition.sort_name is not None + and edition.game.name != edition.sort_name + else "(identical)" + ), + truncate_with_popover(str(edition.platform)), + edition.year_released, + edition.wikidata, + edition.created_at.strftime(dateformat), + render_to_string( + "components/button_group_sm.html", + { + "buttons": [ + { + "href": reverse("edit_edition", args=[edition.pk]), + "text": "Edit", + }, + { + "href": reverse( + "delete_edition", args=[edition.pk] + ), + "text": "Delete", + "color": "red", + }, + ] + }, + ), + ] + for edition in editions + ], + }, + } + return render(request, "list_purchases.html", context) + + +@login_required +def edit_device(request: HttpRequest, edition_id: int = 0) -> HttpResponse: + edition = get_object_or_404(Edition, id=edition_id) + form = EditionForm(request.POST or None, instance=edition) + if form.is_valid(): + form.save() + return redirect("list_editions") + + context: dict[str, Any] = {"form": form, "title": "Edit edition"} + return render(request, "add.html", context) + + +@login_required +def delete_edition(request: HttpRequest, edition_id: int) -> HttpResponse: + edition = get_object_or_404(Edition, id=edition_id) + edition.delete() + return redirect("list_editions") diff --git a/games/migrations/0036_alter_edition_platform.py b/games/migrations/0036_alter_edition_platform.py new file mode 100644 index 0000000..e101e0e --- /dev/null +++ b/games/migrations/0036_alter_edition_platform.py @@ -0,0 +1,19 @@ +# Generated by Django 5.1 on 2024-08-11 16:48 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('games', '0035_alter_session_device'), + ] + + operations = [ + migrations.AlterField( + model_name='edition', + name='platform', + field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.SET_DEFAULT, to='games.platform'), + ), + ] diff --git a/games/models.py b/games/models.py index 540a5fe..15d6dd1 100644 --- a/games/models.py +++ b/games/models.py @@ -39,7 +39,7 @@ class Edition(models.Model): 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.SET_DEFAULT, 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) diff --git a/games/templates/components/popover.html b/games/templates/components/popover.html index 74d4ea0..a19d517 100644 --- a/games/templates/components/popover.html +++ b/games/templates/components/popover.html @@ -1,6 +1,3 @@ - - -