Improve how editions and purchases are displayed
continuous-integration/drone/push Build is passing
Details
continuous-integration/drone/push Build is passing
Details
This commit is contained in:
parent
8982fc5086
commit
e158bc0623
|
@ -1,3 +1,8 @@
|
|||
## Unreleased
|
||||
|
||||
## Improved
|
||||
* game overview: improve how editions and purchases are displayed
|
||||
|
||||
## 1.5.1 / 2023-11-14 21:10+01:00
|
||||
|
||||
## Improved
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
# Generated by Django 4.1.5 on 2023-11-14 21:19
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("games", "0028_purchase_name"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name="purchase",
|
||||
name="related_purchase",
|
||||
field=models.ForeignKey(
|
||||
blank=True,
|
||||
default=None,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
related_name="related_purchases",
|
||||
to="games.purchase",
|
||||
),
|
||||
),
|
||||
]
|
|
@ -124,7 +124,12 @@ class Purchase(models.Model):
|
|||
max_length=255, default="Unknown Name", null=True, blank=True
|
||||
)
|
||||
related_purchase = models.ForeignKey(
|
||||
"Purchase", on_delete=models.SET_NULL, default=None, null=True, blank=True
|
||||
"Purchase",
|
||||
on_delete=models.SET_NULL,
|
||||
default=None,
|
||||
null=True,
|
||||
blank=True,
|
||||
related_name="related_purchases",
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
|
|
|
@ -1429,6 +1429,10 @@ th label {
|
|||
padding-right: 1rem;
|
||||
}
|
||||
|
||||
.sm\:pl-12 {
|
||||
padding-left: 3rem;
|
||||
}
|
||||
|
||||
.sm\:pl-2 {
|
||||
padding-left: 0.5rem;
|
||||
}
|
||||
|
|
|
@ -13,51 +13,47 @@
|
|||
{% include 'components/edit_button.html' with edit_url=edit_url %}
|
||||
</h1>
|
||||
<h2 class="text-lg my-2 ml-2">
|
||||
{{ total_hours }} <span class="dark:text-slate-500">total</span>
|
||||
{{ hours_sum }} <span class="dark:text-slate-500">total</span>
|
||||
{{ session_average }} <span class="dark:text-slate-500">avg</span>
|
||||
({{ playrange }}) </h2>
|
||||
<hr class="border-slate-500">
|
||||
<h1 class="text-3xl mt-4 mb-1">Editions <span class="dark:text-slate-500">({{ editions.count }})</span></h1>
|
||||
<h1 class="text-3xl mt-4 mb-1">Editions <span class="dark:text-slate-500">({{ edition_count }})</span> and Purchases <span class="dark:text-slate-500">({{ purchase_count }})</span></h1>
|
||||
|
||||
<ul>
|
||||
{% for edition in editions %}
|
||||
<li class="sm:pl-2 flex items-center">
|
||||
{{ edition.name }} ({{ edition.platform }}, {{ edition.year_released }})
|
||||
{% if edition.wikidata %}
|
||||
<span class="hidden sm:inline">
|
||||
<a href="https://www.wikidata.org/wiki/{{ edition.wikidata }}">
|
||||
<img class="inline mx-2 w-6" src="{% static 'icons/wikidata.png' %}"/>
|
||||
</a>
|
||||
</span>
|
||||
<span class="hidden sm:inline">
|
||||
<a href="https://www.wikidata.org/wiki/{{ edition.wikidata }}">
|
||||
<img class="inline mx-2 w-6" src="{% static 'icons/wikidata.png' %}"/>
|
||||
</a>
|
||||
</span>
|
||||
{% 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 %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<h1 class="text-3xl mt-4 mb-1">Purchases <span class="dark:text-slate-500">({{ purchases.count }})</span></h1>
|
||||
<ul>
|
||||
{% for purchase in purchases %}
|
||||
<li class="sm:pl-2 flex items-center">
|
||||
{{ purchase.platform }}
|
||||
({{ purchase.get_ownership_type_display }}, {{ purchase.date_purchased | date:"Y" }}, {{ purchase.price }} {{ purchase.price_currency}})
|
||||
{% url 'edit_purchase' purchase.id as edit_url %}
|
||||
{% include 'components/edit_button.html' with edit_url=edit_url %}
|
||||
{% if purchase.related_purchases %}
|
||||
<li>
|
||||
<ul>
|
||||
{% for related_purchase in purchase.related_purchases %}
|
||||
<li class="sm:pl-6 flex items-center">
|
||||
{{ related_purchase.name}} ({{ related_purchase.get_type_display }}, {{ related_purchase.date_purchased | date:"Y" }}, {{ related_purchase.price }} {{ related_purchase.price_currency}})
|
||||
{% url 'edit_purchase' related_purchase.id as edit_url %}
|
||||
{% include 'components/edit_button.html' with edit_url=edit_url %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<ul>
|
||||
{% for purchase in edition.game_purchases %}
|
||||
<li class="sm:pl-6 flex items-center">
|
||||
{{ purchase.get_ownership_type_display }}, {{ purchase.date_purchased | date:"Y" }}, {{ purchase.price }} {{ purchase.price_currency}}
|
||||
{% url 'edit_purchase' purchase.id as edit_url %}
|
||||
{% include 'components/edit_button.html' with edit_url=edit_url %}
|
||||
</li>
|
||||
{% endif %}
|
||||
</li>
|
||||
<ul>
|
||||
{% for related_purchase in purchase.nongame_related_purchases %}
|
||||
<li class="sm:pl-12 flex items-center">
|
||||
{{ related_purchase.name }} ({{ related_purchase.get_type_display }}, {{ purchase.platform }}, {{ related_purchase.date_purchased | date:"Y" }}, {{ related_purchase.price }} {{ related_purchase.price_currency}})
|
||||
{% url 'edit_purchase' related_purchase.id as edit_url %}
|
||||
{% include 'components/edit_button.html' with edit_url=edit_url %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
||||
<h1 class="text-3xl mt-4 mb-1 flex gap-2 items-center">
|
||||
Sessions
|
||||
<span class="dark:text-slate-500">
|
||||
|
|
|
@ -2,7 +2,7 @@ from common.time import format_duration, now as now_with_tz
|
|||
from common.utils import safe_division
|
||||
from datetime import datetime, timedelta
|
||||
from django.conf import settings
|
||||
from django.db.models import Sum, F, Count
|
||||
from django.db.models import Sum, F, Count, Prefetch
|
||||
from django.db.models.functions import TruncDate
|
||||
from django.http import HttpRequest, HttpResponse, HttpResponseRedirect
|
||||
from django.shortcuts import redirect, render
|
||||
|
@ -136,46 +136,52 @@ def edit_game(request, game_id=None):
|
|||
|
||||
|
||||
def view_game(request, game_id=None):
|
||||
context = {}
|
||||
game = Game.objects.get(id=game_id)
|
||||
context["title"] = "View Game"
|
||||
context["game"] = game
|
||||
context["editions"] = Edition.objects.filter(game_id=game_id).order_by(
|
||||
"year_released"
|
||||
nongame_related_purchases_prefetch = Prefetch(
|
||||
"related_purchases",
|
||||
queryset=Purchase.objects.exclude(type=Purchase.GAME),
|
||||
to_attr="nongame_related_purchases",
|
||||
)
|
||||
game_purchases = (
|
||||
Purchase.objects.filter(edition__game_id=game_id)
|
||||
.filter(type=Purchase.GAME)
|
||||
.order_by("date_purchased")
|
||||
game_purchases_prefetch = Prefetch(
|
||||
"purchase_set",
|
||||
queryset=Purchase.objects.filter(type=Purchase.GAME).prefetch_related(
|
||||
nongame_related_purchases_prefetch
|
||||
),
|
||||
to_attr="game_purchases",
|
||||
)
|
||||
editions = (
|
||||
Edition.objects.filter(game=game)
|
||||
.prefetch_related(game_purchases_prefetch)
|
||||
.order_by("year_released")
|
||||
)
|
||||
for purchase in game_purchases:
|
||||
purchase.related_purchases = Purchase.objects.exclude(
|
||||
type=Purchase.GAME
|
||||
).filter(related_purchase=purchase.id)
|
||||
|
||||
context["purchases"] = game_purchases
|
||||
context["sessions"] = Session.objects.filter(
|
||||
purchase__edition__game_id=game_id
|
||||
).order_by("-timestamp_start")
|
||||
context["total_hours"] = float(
|
||||
format_duration(context["sessions"].total_duration_unformatted(), "%2.1H")
|
||||
)
|
||||
context["session_average"] = round(
|
||||
(context["total_hours"]) / int(context["sessions"].count()), 1
|
||||
)
|
||||
# here first and last is flipped
|
||||
# because sessions are ordered from newest to oldest
|
||||
# so the most recent are on top
|
||||
playrange_start = context["sessions"].last().timestamp_start.strftime("%b %Y")
|
||||
playrange_end = context["sessions"].first().timestamp_start.strftime("%b %Y")
|
||||
sessions = Session.objects.filter(purchase__edition__game=game)
|
||||
session_count = sessions.count()
|
||||
|
||||
context["playrange"] = (
|
||||
playrange_start = sessions.first().timestamp_start.strftime("%b %Y")
|
||||
playrange_end = sessions.last().timestamp_start.strftime("%b %Y")
|
||||
|
||||
playrange = (
|
||||
playrange_start
|
||||
if playrange_start == playrange_end
|
||||
else f"{playrange_start} — {playrange_end}"
|
||||
)
|
||||
total_hours = float(format_duration(sessions.total_duration_unformatted(), "%2.1H"))
|
||||
|
||||
context = {
|
||||
"edition_count": editions.count(),
|
||||
"editions": editions,
|
||||
"game": game,
|
||||
"playrange": playrange,
|
||||
"purchase_count": Purchase.objects.filter(edition__game=game).count(),
|
||||
"session_average": round(total_hours / int(session_count), 1),
|
||||
"session_count": session_count,
|
||||
"sessions_with_notes": sessions.exclude(note=""),
|
||||
"sessions": sessions.order_by("-timestamp_start"),
|
||||
"title": f"Game Overview - {game.name}",
|
||||
"hours_sum": total_hours,
|
||||
}
|
||||
|
||||
context["sessions_with_notes"] = context["sessions"].exclude(note="")
|
||||
request.session["return_path"] = request.path
|
||||
return render(request, "view_game.html", context)
|
||||
|
||||
|
|
Loading…
Reference in New Issue