diff --git a/CHANGELOG.md b/CHANGELOG.md
index e511804..4f0d3e3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -17,8 +17,11 @@
* All finished games
* All finished 2023 games
* All finished games that were purchased this year
- * Total sessions
+ * Sessions (count)
* Days played
+ * Finished (count)
+ * Unfinished (count)
+ * Refunded (count)
### Improved
* game overview: simplify playtime range display
diff --git a/games/models.py b/games/models.py
index 082631d..c16943f 100644
--- a/games/models.py
+++ b/games/models.py
@@ -33,6 +33,17 @@ class Edition(models.Model):
return self.name
+class PurchaseQueryset(models.QuerySet):
+ def refunded(self):
+ return self.filter(date_refunded__isnull=False)
+
+ def not_refunded(self):
+ return self.filter(date_refunded__isnull=True)
+
+ def finished(self):
+ return self.filter(date_finished__isnull=False)
+
+
class Purchase(models.Model):
PHYSICAL = "ph"
DIGITAL = "di"
@@ -53,6 +64,8 @@ class Purchase(models.Model):
(PIRATED, "Pirated"),
]
+ objects = PurchaseQueryset().as_manager()
+
edition = models.ForeignKey("Edition", on_delete=models.CASCADE)
platform = models.ForeignKey(
"Platform", on_delete=models.CASCADE, default=None, null=True, blank=True
diff --git a/games/static/base.css b/games/static/base.css
index e159d66..9790306 100644
--- a/games/static/base.css
+++ b/games/static/base.css
@@ -1438,6 +1438,10 @@ th label {
display: block;
}
+ .md\:w-1\/2 {
+ width: 50%;
+ }
+
.md\:w-auto {
width: auto;
}
diff --git a/games/templates/stats.html b/games/templates/stats.html
index 08816b3..48acd3c 100644
--- a/games/templates/stats.html
+++ b/games/templates/stats.html
@@ -16,47 +16,62 @@
-
-
-
- Statistic |
- Value |
-
-
-
- Hours |
- {{ total_hours }} |
-
-
- Sessions |
- {{ total_sessions }} |
-
-
- Days Played |
- {{ unique_days }} ({{ unique_days_percent }}%) |
-
-
- Games |
- {{ total_games }} |
-
-
- Games ({{ year }}) |
- {{ total_2023_games }} |
-
-
- Purchases |
- {{ all_purchased_this_year.count }} |
-
-
- Spendings ({{ total_spent_currency }}) |
- {{ total_spent }} |
-
-
- {{ total_spent_currency }}/game |
- {{ spent_per_game }} |
-
-
-
+
+
+
Playtime
+
+
+
+ Hours |
+ {{ total_hours }} |
+
+
+ Sessions |
+ {{ total_sessions }} |
+
+
+ Days |
+ {{ unique_days }} ({{ unique_days_percent }}%) |
+
+
+ Games |
+ {{ total_games }} |
+
+
+ Games ({{ year }}) |
+ {{ total_2023_games }} |
+
+
+ Finished |
+ {{ all_finished_this_year.count }} |
+
+
+
+
+
+
Purchases
+
+
+
+ Total |
+ {{ all_purchased_this_year.count }} |
+
+
+ Refunded |
+ {{ all_purchased_refunded_this_year.count }} ({{ refunded_percent }}%) |
+
+
+ Unfinished |
+ {{ purchased_unfinished.count }} ({{ unfinished_purchases_percent }}%) |
+
+
+ Spendings ({{ total_spent_currency }}) |
+ {{ total_spent }} ({{ spent_per_game }}/game) |
+
+
+
+
+
Top games by playtime
diff --git a/games/views.py b/games/views.py
index 6989c2d..5196801 100644
--- a/games/views.py
+++ b/games/views.py
@@ -291,19 +291,40 @@ def stats(request, year: int = 0):
all_purchased_this_year = (
Purchase.objects.filter(date_purchased__year=year)
.filter(price_currency__exact=selected_currency)
- .filter(date_refunded__exact=None)
+ .order_by("date_purchased")
+ )
+ all_purchased_without_refunded_this_year = all_purchased_this_year.not_refunded()
+ all_purchased_refunded_this_year = (
+ Purchase.objects.filter(date_purchased__year=year)
+ .filter(price_currency__exact=selected_currency)
+ .refunded()
.order_by("date_purchased")
)
- all_finished_this_year = Purchase.objects.filter(date_finished__year=year)
- this_year_finished_this_year = Purchase.objects.filter(
- date_finished__year=year
- ).filter(edition__year_released=year)
- purchased_this_year_finished_this_year = all_purchased_this_year.filter(
- date_finished__year=year
+ purchased_unfinished = all_purchased_without_refunded_this_year.filter(
+ date_finished__isnull=True
+ )
+ unfinished_purchases_percent = int(
+ purchased_unfinished.count() / all_purchased_refunded_this_year.count() * 100
)
- this_year_spendings = all_purchased_this_year.aggregate(total_spent=Sum(F("price")))
+ all_finished_this_year = Purchase.objects.filter(date_finished__year=year).order_by(
+ "date_finished"
+ )
+ this_year_finished_this_year = (
+ Purchase.objects.filter(date_finished__year=year)
+ .filter(edition__year_released=year)
+ .order_by("date_finished")
+ )
+ purchased_this_year_finished_this_year = (
+ all_purchased_without_refunded_this_year.filter(
+ date_finished__year=year
+ ).order_by("date_finished")
+ )
+
+ this_year_spendings = all_purchased_without_refunded_this_year.aggregate(
+ total_spent=Sum(F("price"))
+ )
total_spent = this_year_spendings["total_spent"]
games_with_playtime = (
@@ -343,14 +364,25 @@ def stats(request, year: int = 0):
"total_playtime_per_platform": total_playtime_per_platform,
"total_spent": total_spent,
"total_spent_currency": selected_currency,
- "all_purchased_this_year": all_purchased_this_year,
- "spent_per_game": int(total_spent / all_purchased_this_year.count()),
+ "all_purchased_this_year": all_purchased_without_refunded_this_year,
+ "spent_per_game": int(
+ total_spent / all_purchased_without_refunded_this_year.count()
+ ),
"all_finished_this_year": all_finished_this_year,
"this_year_finished_this_year": this_year_finished_this_year,
"purchased_this_year_finished_this_year": purchased_this_year_finished_this_year,
"total_sessions": year_sessions.count(),
"unique_days": unique_days["dates"],
"unique_days_percent": int(unique_days["dates"] / 365 * 100),
+ "purchased_unfinished": purchased_unfinished,
+ "unfinished_purchases_percent": unfinished_purchases_percent,
+ "refunded_percent": int(
+ all_purchased_refunded_this_year.count()
+ / all_purchased_this_year.count()
+ * 100
+ ),
+ "all_purchased_refunded_this_year": all_purchased_refunded_this_year,
+ "all_purchased_this_year": all_purchased_this_year,
}
request.session["return_path"] = request.path