Add more stats
continuous-integration/drone/push Build is passing
Details
continuous-integration/drone/push Build is passing
Details
* Finished (count) * Unfinished (count) * Refunded (count)
This commit is contained in:
parent
5052ca7dbf
commit
e7ed349356
|
@ -17,8 +17,11 @@
|
||||||
* All finished games
|
* All finished games
|
||||||
* All finished 2023 games
|
* All finished 2023 games
|
||||||
* All finished games that were purchased this year
|
* All finished games that were purchased this year
|
||||||
* Total sessions
|
* Sessions (count)
|
||||||
* Days played
|
* Days played
|
||||||
|
* Finished (count)
|
||||||
|
* Unfinished (count)
|
||||||
|
* Refunded (count)
|
||||||
|
|
||||||
### Improved
|
### Improved
|
||||||
* game overview: simplify playtime range display
|
* game overview: simplify playtime range display
|
||||||
|
|
|
@ -33,6 +33,17 @@ class Edition(models.Model):
|
||||||
return self.name
|
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):
|
class Purchase(models.Model):
|
||||||
PHYSICAL = "ph"
|
PHYSICAL = "ph"
|
||||||
DIGITAL = "di"
|
DIGITAL = "di"
|
||||||
|
@ -53,6 +64,8 @@ class Purchase(models.Model):
|
||||||
(PIRATED, "Pirated"),
|
(PIRATED, "Pirated"),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
objects = PurchaseQueryset().as_manager()
|
||||||
|
|
||||||
edition = models.ForeignKey("Edition", on_delete=models.CASCADE)
|
edition = models.ForeignKey("Edition", on_delete=models.CASCADE)
|
||||||
platform = models.ForeignKey(
|
platform = models.ForeignKey(
|
||||||
"Platform", on_delete=models.CASCADE, default=None, null=True, blank=True
|
"Platform", on_delete=models.CASCADE, default=None, null=True, blank=True
|
||||||
|
|
|
@ -1438,6 +1438,10 @@ th label {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.md\:w-1\/2 {
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
.md\:w-auto {
|
.md\:w-auto {
|
||||||
width: auto;
|
width: auto;
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,47 +16,62 @@
|
||||||
</select>
|
</select>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<table class="responsive-table">
|
<div class="flex flex-column flex-wrap justify-center">
|
||||||
<thead>
|
<div class="md:w-1/2">
|
||||||
<tr>
|
<h1 class="text-5xl text-center my-6">Playtime</h1>
|
||||||
<th class="px-2 sm:px-4 md:px-6 md:py-2">Statistic</th>
|
<table class="responsive-table">
|
||||||
<th class="px-2 sm:px-4 md:px-6 md:py-2">Value</th>
|
<tbody>
|
||||||
</tr>
|
<tr>
|
||||||
<tbody>
|
<td class="px-2 sm:px-4 md:px-6 md:py-2">Hours</td>
|
||||||
<tr>
|
<td class="px-2 sm:px-4 md:px-6 md:py-2 font-mono">{{ total_hours }}</td>
|
||||||
<td class="px-2 sm:px-4 md:px-6 md:py-2">Hours</td>
|
</tr>
|
||||||
<td class="px-2 sm:px-4 md:px-6 md:py-2 font-mono">{{ total_hours }}</td>
|
<tr>
|
||||||
</tr>
|
<td class="px-2 sm:px-4 md:px-6 md:py-2">Sessions</td>
|
||||||
<tr>
|
<td class="px-2 sm:px-4 md:px-6 md:py-2 font-mono">{{ total_sessions }}</td>
|
||||||
<td class="px-2 sm:px-4 md:px-6 md:py-2">Sessions</td>
|
</tr>
|
||||||
<td class="px-2 sm:px-4 md:px-6 md:py-2 font-mono">{{ total_sessions }}</td>
|
<tr>
|
||||||
</tr>
|
<td class="px-2 sm:px-4 md:px-6 md:py-2">Days</td>
|
||||||
<tr>
|
<td class="px-2 sm:px-4 md:px-6 md:py-2 font-mono">{{ unique_days }} ({{ unique_days_percent }}%)</td>
|
||||||
<td class="px-2 sm:px-4 md:px-6 md:py-2">Days Played</td>
|
</tr>
|
||||||
<td class="px-2 sm:px-4 md:px-6 md:py-2 font-mono">{{ unique_days }} ({{ unique_days_percent }}%)</td>
|
<tr>
|
||||||
</tr>
|
<td class="px-2 sm:px-4 md:px-6 md:py-2">Games</td>
|
||||||
<tr>
|
<td class="px-2 sm:px-4 md:px-6 md:py-2 font-mono">{{ total_games }}</td>
|
||||||
<td class="px-2 sm:px-4 md:px-6 md:py-2">Games</td>
|
</tr>
|
||||||
<td class="px-2 sm:px-4 md:px-6 md:py-2 font-mono">{{ total_games }}</td>
|
<tr>
|
||||||
</tr>
|
<td class="px-2 sm:px-4 md:px-6 md:py-2">Games ({{ year }})</td>
|
||||||
<tr>
|
<td class="px-2 sm:px-4 md:px-6 md:py-2 font-mono">{{ total_2023_games }}</td>
|
||||||
<td class="px-2 sm:px-4 md:px-6 md:py-2">Games ({{ year }})</td>
|
</tr>
|
||||||
<td class="px-2 sm:px-4 md:px-6 md:py-2 font-mono">{{ total_2023_games }}</td>
|
<tr>
|
||||||
</tr>
|
<td class="px-2 sm:px-4 md:px-6 md:py-2">Finished</td>
|
||||||
<tr>
|
<td class="px-2 sm:px-4 md:px-6 md:py-2 font-mono">{{ all_finished_this_year.count }}</td>
|
||||||
<td class="px-2 sm:px-4 md:px-6 md:py-2">Purchases</td>
|
</tr>
|
||||||
<td class="px-2 sm:px-4 md:px-6 md:py-2 font-mono">{{ all_purchased_this_year.count }}</td>
|
</tbody>
|
||||||
</tr>
|
</table>
|
||||||
<tr>
|
</div>
|
||||||
<td class="px-2 sm:px-4 md:px-6 md:py-2">Spendings ({{ total_spent_currency }})</td>
|
<div class="md:w-1/2">
|
||||||
<td class="px-2 sm:px-4 md:px-6 md:py-2 font-mono">{{ total_spent }}</td>
|
<h1 class="text-5xl text-center my-6">Purchases</h1>
|
||||||
</tr>
|
<table class="responsive-table">
|
||||||
<tr>
|
<tbody>
|
||||||
<td class="px-2 sm:px-4 md:px-6 md:py-2">{{ total_spent_currency }}/game</td>
|
<tr>
|
||||||
<td class="px-2 sm:px-4 md:px-6 md:py-2 font-mono">{{ spent_per_game }}</td>
|
<td class="px-2 sm:px-4 md:px-6 md:py-2">Total</td>
|
||||||
</tr>
|
<td class="px-2 sm:px-4 md:px-6 md:py-2 font-mono">{{ all_purchased_this_year.count }}</td>
|
||||||
</tbody>
|
</tr>
|
||||||
</table>
|
<tr>
|
||||||
|
<td class="px-2 sm:px-4 md:px-6 md:py-2">Refunded</td>
|
||||||
|
<td class="px-2 sm:px-4 md:px-6 md:py-2 font-mono">{{ all_purchased_refunded_this_year.count }} ({{ refunded_percent }}%)</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="px-2 sm:px-4 md:px-6 md:py-2">Unfinished</td>
|
||||||
|
<td class="px-2 sm:px-4 md:px-6 md:py-2 font-mono">{{ purchased_unfinished.count }} ({{ unfinished_purchases_percent }}%)</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="px-2 sm:px-4 md:px-6 md:py-2">Spendings ({{ total_spent_currency }})</td>
|
||||||
|
<td class="px-2 sm:px-4 md:px-6 md:py-2 font-mono">{{ total_spent }} ({{ spent_per_game }}/game)</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<h1 class="text-5xl text-center my-6">Top games by playtime</h1>
|
<h1 class="text-5xl text-center my-6">Top games by playtime</h1>
|
||||||
<table class="responsive-table">
|
<table class="responsive-table">
|
||||||
<thead>
|
<thead>
|
||||||
|
|
|
@ -291,19 +291,40 @@ def stats(request, year: int = 0):
|
||||||
all_purchased_this_year = (
|
all_purchased_this_year = (
|
||||||
Purchase.objects.filter(date_purchased__year=year)
|
Purchase.objects.filter(date_purchased__year=year)
|
||||||
.filter(price_currency__exact=selected_currency)
|
.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")
|
.order_by("date_purchased")
|
||||||
)
|
)
|
||||||
|
|
||||||
all_finished_this_year = Purchase.objects.filter(date_finished__year=year)
|
purchased_unfinished = all_purchased_without_refunded_this_year.filter(
|
||||||
this_year_finished_this_year = Purchase.objects.filter(
|
date_finished__isnull=True
|
||||||
date_finished__year=year
|
)
|
||||||
).filter(edition__year_released=year)
|
unfinished_purchases_percent = int(
|
||||||
purchased_this_year_finished_this_year = all_purchased_this_year.filter(
|
purchased_unfinished.count() / all_purchased_refunded_this_year.count() * 100
|
||||||
date_finished__year=year
|
|
||||||
)
|
)
|
||||||
|
|
||||||
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"]
|
total_spent = this_year_spendings["total_spent"]
|
||||||
|
|
||||||
games_with_playtime = (
|
games_with_playtime = (
|
||||||
|
@ -343,14 +364,25 @@ def stats(request, year: int = 0):
|
||||||
"total_playtime_per_platform": total_playtime_per_platform,
|
"total_playtime_per_platform": total_playtime_per_platform,
|
||||||
"total_spent": total_spent,
|
"total_spent": total_spent,
|
||||||
"total_spent_currency": selected_currency,
|
"total_spent_currency": selected_currency,
|
||||||
"all_purchased_this_year": all_purchased_this_year,
|
"all_purchased_this_year": all_purchased_without_refunded_this_year,
|
||||||
"spent_per_game": int(total_spent / all_purchased_this_year.count()),
|
"spent_per_game": int(
|
||||||
|
total_spent / all_purchased_without_refunded_this_year.count()
|
||||||
|
),
|
||||||
"all_finished_this_year": all_finished_this_year,
|
"all_finished_this_year": all_finished_this_year,
|
||||||
"this_year_finished_this_year": this_year_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,
|
"purchased_this_year_finished_this_year": purchased_this_year_finished_this_year,
|
||||||
"total_sessions": year_sessions.count(),
|
"total_sessions": year_sessions.count(),
|
||||||
"unique_days": unique_days["dates"],
|
"unique_days": unique_days["dates"],
|
||||||
"unique_days_percent": int(unique_days["dates"] / 365 * 100),
|
"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
|
request.session["return_path"] = request.path
|
||||||
|
|
Loading…
Reference in New Issue