Fix code smells

This commit is contained in:
2026-06-06 08:15:19 +02:00
parent d101aecd70
commit 36b1382015
9 changed files with 59 additions and 105 deletions
+23
View File
@@ -5,11 +5,34 @@ from functools import reduce, wraps
from typing import Any, Callable, Generator, Literal, TypeVar from typing import Any, Callable, Generator, Literal, TypeVar
from urllib.parse import urlencode from urllib.parse import urlencode
from django.core.paginator import Page, Paginator
from django.db.models import Q from django.db.models import Q
from django.http import HttpRequest from django.http import HttpRequest
from django.shortcuts import redirect from django.shortcuts import redirect
def paginate(request: HttpRequest, queryset, per_page: int = 10):
"""Standard list-view pagination.
Reads ``page`` and ``limit`` from the query string (``limit=0`` disables
pagination) and returns ``(object_list, page_obj, elided_page_range)`` ready
to hand to ``paginated_table_content``.
"""
page_number = request.GET.get("page", 1)
limit = int(request.GET.get("limit", per_page))
object_list = queryset
page_obj: Page | None = None
if limit != 0:
page_obj = Paginator(queryset, limit).get_page(page_number)
object_list = page_obj.object_list
elided_page_range = (
page_obj.paginator.get_elided_page_range(page_number, on_each_side=1, on_ends=1)
if page_obj
else None
)
return object_list, page_obj, elided_page_range
def safe_division(numerator: int | float, denominator: int | float) -> int | float: def safe_division(numerator: int | float, denominator: int | float) -> int | float:
""" """
Divides without triggering division by zero exception. Divides without triggering division by zero exception.
+3 -13
View File
@@ -1,5 +1,4 @@
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.core.paginator import Paginator
from django.http import HttpRequest, HttpResponse from django.http import HttpRequest, HttpResponse
from django.shortcuts import get_object_or_404, redirect from django.shortcuts import get_object_or_404, redirect
from django.urls import reverse from django.urls import reverse
@@ -14,24 +13,15 @@ from common.components import (
) )
from common.layout import render_page from common.layout import render_page
from common.time import dateformat, local_strftime from common.time import dateformat, local_strftime
from common.utils import paginate
from games.forms import DeviceForm from games.forms import DeviceForm
from games.models import Device from games.models import Device
@login_required @login_required
def list_devices(request: HttpRequest) -> HttpResponse: def list_devices(request: HttpRequest) -> HttpResponse:
page_number = request.GET.get("page", 1) devices, page_obj, elided_page_range = paginate(
limit = request.GET.get("limit", 10) request, Device.objects.order_by("-created_at")
devices = Device.objects.order_by("-created_at")
page_obj = None
if int(limit) != 0:
paginator = Paginator(devices, limit)
page_obj = paginator.get_page(page_number)
devices = page_obj.object_list
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 = { data = {
+2 -14
View File
@@ -41,7 +41,7 @@ from common.time import (
local_strftime, local_strftime,
timeformat, timeformat,
) )
from common.utils import build_dynamic_filter, safe_division, truncate from common.utils import build_dynamic_filter, paginate, safe_division, truncate
from games.forms import GameForm from games.forms import GameForm
from games.models import Game from games.models import Game
from games.views.general import use_custom_redirect from games.views.general import use_custom_redirect
@@ -50,10 +50,7 @@ from games.views.playevent import create_playevent_tabledata
@login_required @login_required
def list_games(request: HttpRequest, search_string: str = "") -> HttpResponse: def list_games(request: HttpRequest, search_string: str = "") -> HttpResponse:
page_number = request.GET.get("page", 1)
limit = request.GET.get("limit", 10)
games = Game.objects.order_by("-created_at") games = Game.objects.order_by("-created_at")
page_obj = None
search_string = request.GET.get("search_string", search_string) search_string = request.GET.get("search_string", search_string)
if search_string != "": if search_string != "":
filters = [ filters = [
@@ -74,16 +71,7 @@ def list_games(request: HttpRequest, search_string: str = "") -> HttpResponse:
search_status = Game.Status[search_string.upper()] search_status = Game.Status[search_string.upper()]
filters.append(Q(status=search_status)) filters.append(Q(status=search_status))
games = games.filter(build_dynamic_filter(filters, "|")) games = games.filter(build_dynamic_filter(filters, "|"))
if int(limit) != 0: games, page_obj, elided_page_range = paginate(request, games)
paginator = Paginator(games, limit)
page_obj = paginator.get_page(page_number)
games = page_obj.object_list
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 = { data = {
"header_action": Div( "header_action": Div(
+15 -9
View File
@@ -109,7 +109,8 @@ def stats_alltime(request: HttpRequest) -> HttpResponse:
this_year_purchases_unfinished_dropped_nondropped = ( this_year_purchases_unfinished_dropped_nondropped = (
this_year_purchases_without_refunded.filter( this_year_purchases_without_refunded.filter(
~Q(games__status="f") & ~Q(games__playevents__ended__isnull=False) ~Q(games__status=Game.Status.FINISHED)
& ~Q(games__playevents__ended__isnull=False)
) )
.filter(infinite=False) .filter(infinite=False)
.filter(Q(type=Purchase.GAME) | Q(type=Purchase.DLC)) .filter(Q(type=Purchase.GAME) | Q(type=Purchase.DLC))
@@ -117,14 +118,16 @@ def stats_alltime(request: HttpRequest) -> HttpResponse:
this_year_purchases_unfinished = ( this_year_purchases_unfinished = (
this_year_purchases_unfinished_dropped_nondropped.filter( this_year_purchases_unfinished_dropped_nondropped.filter(
~Q(games__status="r") & ~Q(games__status="a") ~Q(games__status=Game.Status.RETIRED)
& ~Q(games__status=Game.Status.ABANDONED)
) )
) )
this_year_purchases_dropped = ( this_year_purchases_dropped = (
this_year_purchases.filter( this_year_purchases.filter(
~Q(games__status="f") & ~Q(games__playevents__ended__isnull=False) ~Q(games__status=Game.Status.FINISHED)
& ~Q(games__playevents__ended__isnull=False)
) )
.filter(Q(games__status="a") | Q(date_refunded__isnull=False)) .filter(Q(games__status=Game.Status.ABANDONED) | Q(date_refunded__isnull=False))
.filter(infinite=False) .filter(infinite=False)
.filter(Q(type=Purchase.GAME) | Q(type=Purchase.DLC)) .filter(Q(type=Purchase.GAME) | Q(type=Purchase.DLC))
) )
@@ -338,7 +341,8 @@ def stats(request: HttpRequest, year: int = 0) -> HttpResponse:
# only Game and DLC # only Game and DLC
this_year_purchases_unfinished_dropped_nondropped = ( this_year_purchases_unfinished_dropped_nondropped = (
this_year_purchases_without_refunded.filter( this_year_purchases_without_refunded.filter(
~Q(games__status="f") & ~Q(games__playevents__ended__year=year) ~Q(games__status=Game.Status.FINISHED)
& ~Q(games__playevents__ended__year=year)
) )
.filter(infinite=False) .filter(infinite=False)
.filter(Q(type=Purchase.GAME) | Q(type=Purchase.DLC)) .filter(Q(type=Purchase.GAME) | Q(type=Purchase.DLC))
@@ -347,15 +351,17 @@ def stats(request: HttpRequest, year: int = 0) -> HttpResponse:
# unfinished = not finished AND not dropped # unfinished = not finished AND not dropped
this_year_purchases_unfinished = ( this_year_purchases_unfinished = (
this_year_purchases_unfinished_dropped_nondropped.filter( this_year_purchases_unfinished_dropped_nondropped.filter(
~Q(games__status="r") & ~Q(games__status="a") ~Q(games__status=Game.Status.RETIRED)
& ~Q(games__status=Game.Status.ABANDONED)
) )
) )
# dropped = abandoned OR retired OR refunded (OR logic for transition) # dropped = abandoned OR retired OR refunded (OR logic for transition)
this_year_purchases_dropped = ( this_year_purchases_dropped = (
this_year_purchases.filter( this_year_purchases.filter(
~Q(games__status="f") & ~Q(games__playevents__ended__year=year) ~Q(games__status=Game.Status.FINISHED)
& ~Q(games__playevents__ended__year=year)
) )
.filter(Q(games__status="a") | Q(date_refunded__isnull=False)) .filter(Q(games__status=Game.Status.ABANDONED) | Q(date_refunded__isnull=False))
.filter(infinite=False) .filter(infinite=False)
.filter(Q(type=Purchase.GAME) | Q(type=Purchase.DLC)) .filter(Q(type=Purchase.GAME) | Q(type=Purchase.DLC))
) )
@@ -432,7 +438,7 @@ def stats(request: HttpRequest, year: int = 0) -> HttpResponse:
backlog_decrease_count = ( backlog_decrease_count = (
Purchase.objects.filter(date_purchased__year__lt=year) Purchase.objects.filter(date_purchased__year__lt=year)
.filter(games__status="f") .filter(games__status=Game.Status.FINISHED)
.filter(games__playevents__ended__year=year) .filter(games__playevents__ended__year=year)
.count() .count()
) )
+3 -13
View File
@@ -1,5 +1,4 @@
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.core.paginator import Paginator
from django.http import HttpRequest, HttpResponse from django.http import HttpRequest, HttpResponse
from django.shortcuts import get_object_or_404, redirect from django.shortcuts import get_object_or_404, redirect
from django.urls import reverse from django.urls import reverse
@@ -14,6 +13,7 @@ from common.components import (
) )
from common.layout import render_page from common.layout import render_page
from common.time import dateformat, local_strftime from common.time import dateformat, local_strftime
from common.utils import paginate
from games.forms import PlatformForm from games.forms import PlatformForm
from games.models import Platform from games.models import Platform
from games.views.general import use_custom_redirect from games.views.general import use_custom_redirect
@@ -21,18 +21,8 @@ from games.views.general import use_custom_redirect
@login_required @login_required
def list_platforms(request: HttpRequest) -> HttpResponse: def list_platforms(request: HttpRequest) -> HttpResponse:
page_number = request.GET.get("page", 1) platforms, page_obj, elided_page_range = paginate(
limit = request.GET.get("limit", 10) request, Platform.objects.order_by("name")
platforms = Platform.objects.order_by("name")
page_obj = None
if int(limit) != 0:
paginator = Paginator(platforms, limit)
page_obj = paginator.get_page(page_number)
platforms = page_obj.object_list
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 = { data = {
+3 -13
View File
@@ -3,7 +3,6 @@ from datetime import datetime, timedelta
from typing import Any, Callable, TypedDict from typing import Any, Callable, TypedDict
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.core.paginator import Paginator
from django.db.models import QuerySet from django.db.models import QuerySet
from django.db.models.manager import BaseManager from django.db.models.manager import BaseManager
from django.http import HttpRequest, HttpResponse, HttpResponseRedirect from django.http import HttpRequest, HttpResponse, HttpResponseRedirect
@@ -20,6 +19,7 @@ from common.components import (
) )
from common.layout import render_page from common.layout import render_page
from common.time import dateformat, format_duration, local_strftime from common.time import dateformat, format_duration, local_strftime
from common.utils import paginate
from games.forms import PlayEventForm from games.forms import PlayEventForm
from games.models import Game, PlayEvent, Session from games.models import Game, PlayEvent, Session
@@ -125,18 +125,8 @@ def _get_formatted_playtime_for_game_sessions_in_range(
@login_required @login_required
def list_playevents(request: HttpRequest) -> HttpResponse: def list_playevents(request: HttpRequest) -> HttpResponse:
page_number = request.GET.get("page", 1) playevents, page_obj, elided_page_range = paginate(
limit = request.GET.get("limit", 10) request, PlayEvent.objects.order_by("-created_at")
playevents = PlayEvent.objects.order_by("-created_at")
page_obj = None
if int(limit) != 0:
paginator = Paginator(playevents, limit)
page_obj = paginator.get_page(page_number)
playevents = page_obj.object_list
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 = create_playevent_tabledata(playevents, request=request) data = create_playevent_tabledata(playevents, request=request)
content = paginated_table_content( content = paginated_table_content(
+3 -13
View File
@@ -1,6 +1,5 @@
from django.contrib import messages from django.contrib import messages
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.core.paginator import Paginator
from django.http import ( from django.http import (
HttpRequest, HttpRequest,
HttpResponse, HttpResponse,
@@ -35,6 +34,7 @@ from common.components import (
) )
from common.layout import render_page from common.layout import render_page
from common.time import dateformat from common.time import dateformat
from common.utils import paginate
from games.forms import PurchaseForm from games.forms import PurchaseForm
from games.models import Game, Purchase from games.models import Game, Purchase
from games.views.general import use_custom_redirect from games.views.general import use_custom_redirect
@@ -95,18 +95,8 @@ def _render_purchase_row(purchase):
@login_required @login_required
def list_purchases(request: HttpRequest) -> HttpResponse: def list_purchases(request: HttpRequest) -> HttpResponse:
page_number = request.GET.get("page", 1) purchases, page_obj, elided_page_range = paginate(
limit = request.GET.get("limit", 10) request, Purchase.objects.order_by("-date_purchased", "-created_at")
purchases = Purchase.objects.order_by("-date_purchased", "-created_at")
page_obj = None
if int(limit) != 0:
paginator = Paginator(purchases, limit)
page_obj = paginator.get_page(page_number)
purchases = page_obj.object_list
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 = { data = {
+2 -15
View File
@@ -1,7 +1,6 @@
from typing import Any from typing import Any
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.core.paginator import Paginator
from django.db.models import Q from django.db.models import Q
from django.http import HttpRequest, HttpResponse from django.http import HttpRequest, HttpResponse
from django.middleware.csrf import get_token from django.middleware.csrf import get_token
@@ -32,15 +31,13 @@ from common.time import (
local_strftime, local_strftime,
timeformat, timeformat,
) )
from common.utils import truncate from common.utils import paginate, truncate
from games.forms import SessionForm from games.forms import SessionForm
from games.models import Device, Game, Session from games.models import Device, Game, Session
@login_required @login_required
def list_sessions(request: HttpRequest, search_string: str = "") -> HttpResponse: def list_sessions(request: HttpRequest, search_string: str = "") -> HttpResponse:
page_number = request.GET.get("page", 1)
limit = request.GET.get("limit", 10)
sessions = Session.objects.order_by("-timestamp_start", "created_at") sessions = Session.objects.order_by("-timestamp_start", "created_at")
device_list = Device.objects.order_by("name") device_list = Device.objects.order_by("name")
search_string = request.GET.get("search_string", search_string) search_string = request.GET.get("search_string", search_string)
@@ -56,17 +53,7 @@ def list_sessions(request: HttpRequest, search_string: str = "") -> HttpResponse
last_session = sessions.latest() last_session = sessions.latest()
except Session.DoesNotExist: except Session.DoesNotExist:
last_session = None last_session = None
page_obj = None sessions, page_obj, elided_page_range = paginate(request, sessions)
if int(limit) != 0:
paginator = Paginator(sessions, limit)
page_obj = paginator.get_page(page_number)
sessions = page_obj.object_list
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 = { data = {
"header_action": Div( "header_action": Div(
+5 -15
View File
@@ -1,5 +1,4 @@
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.core.paginator import Paginator
from django.http import HttpRequest, HttpResponse from django.http import HttpRequest, HttpResponse
from django.shortcuts import get_object_or_404, redirect from django.shortcuts import get_object_or_404, redirect
from django.urls import reverse from django.urls import reverse
@@ -16,6 +15,7 @@ from common.components import (
) )
from common.layout import render_page from common.layout import render_page
from common.time import dateformat, local_strftime from common.time import dateformat, local_strftime
from common.utils import paginate
from games.forms import GameStatusChangeForm from games.forms import GameStatusChangeForm
from games.models import GameStatusChange from games.models import GameStatusChange
@@ -36,8 +36,8 @@ def edit_statuschange(request: HttpRequest, statuschange_id: int) -> HttpRespons
statuschange = get_object_or_404(GameStatusChange, id=statuschange_id) statuschange = get_object_or_404(GameStatusChange, id=statuschange_id)
form = GameStatusChangeForm(request.POST or None, instance=statuschange) form = GameStatusChangeForm(request.POST or None, instance=statuschange)
if form.is_valid(): if form.is_valid():
form.save() saved = form.save()
return redirect("games:list_platforms") return redirect("games:view_game", game_id=saved.game.id)
return render_page( return render_page(
request, AddForm(form, request=request), title="Edit status change" request, AddForm(form, request=request), title="Edit status change"
) )
@@ -45,18 +45,8 @@ def edit_statuschange(request: HttpRequest, statuschange_id: int) -> HttpRespons
@login_required @login_required
def list_statuschanges(request: HttpRequest) -> HttpResponse: def list_statuschanges(request: HttpRequest) -> HttpResponse:
page_number = request.GET.get("page", 1) statuschanges, page_obj, elided_page_range = paginate(
limit = request.GET.get("limit", 10) request, GameStatusChange.objects.select_related("game").all()
statuschanges = GameStatusChange.objects.select_related("game").all()
page_obj = None
if int(limit) != 0:
paginator = Paginator(statuschanges, limit)
page_obj = paginator.get_page(page_number)
statuschanges = page_obj.object_list
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 = { data = {