Files
MarietjeDjango/marietje/stats/utils.py

286 lines
12 KiB
Python

from datetime import datetime, timedelta
from django.core.cache import caches
from django.conf import settings
from django.db.models import Count, Q, Sum
from django.utils import timezone
from queues.models import PlaylistSong
from songs.models import Song
from marietje.models import User
CACHE_TTL = 2 * 24 * 3600
def recache_stats():
new_stats = compute_stats()
caches["default"].delete("stats")
caches["default"].set("stats", new_stats, CACHE_TTL)
return new_stats
def recache_user_stats():
users = User.objects.exclude(Q(id=None) | Q(id__in=settings.STATS_REQUEST_IGNORE_USER_IDS)).values("id")
for user in users:
new_stats = user_stats(user["id"])
cacheloc = "userstats_{}".format(user["id"])
caches["userstats"].delete(cacheloc)
caches["userstats"].set(cacheloc, new_stats, CACHE_TTL)
return new_stats
def best_uploaders_list(requests_uploader, most_requested_uploaders):
for requests in requests_uploader:
a = len(most_requested_uploaders)
b = 0
while b <= a:
if b == a:
adding_list_item(most_requested_uploaders, requests)
elif requests["song__user__id"] == most_requested_uploaders[b]["id"]:
if requests["song__user__name"] == requests["user__name"]:
most_requested_uploaders[b]["own_total"] = requests["total"]
else:
most_requested_uploaders[b]["total"] += requests["total"]
break
b += 1
def adding_list_item(most_requested_list, requests):
# adds a single item to the best_uploaders list
most_requested_list.append(
{"id": requests["song__user__id"], "name": requests["song__user__name"], "total": 0, "own_total": 0}
)
if requests["song__user__id"] == requests["user__id"]:
most_requested_list[-1]["own_total"] = requests["total"]
else:
most_requested_list[-1]["total"] = requests["total"]
def compute_stats():
# We want to grab the time now, because otherwise we would be reporting a minute too late
last_updated = datetime.now()
stats = {}
stats["total_uploads"] = Song.objects.filter(deleted=False).exclude(user_id=None).count()
stats["upload_stats"] = (
Song.objects.filter(deleted=False)
.exclude(user_id=None)
.values("user__id", "user__name")
.annotate(total=Count("id"))
.order_by("-total", "user__name")[: settings.STATS_TOP_COUNT]
)
stats["total_requests"] = (
PlaylistSong.objects.filter(state=2)
.exclude(Q(user_id=None) | Q(user_id__in=settings.STATS_REQUEST_IGNORE_USER_IDS))
.count()
)
stats["request_stats"] = (
PlaylistSong.objects.filter(state=2)
.exclude(Q(user_id=None) | Q(user_id__in=settings.STATS_REQUEST_IGNORE_USER_IDS))
.values("user__id", "user__name")
.annotate(total=Count("id"))
.order_by("-total", "user__name")[: settings.STATS_TOP_COUNT]
)
stats["unique_request_stats"] = (
PlaylistSong.objects.filter(state=2)
.exclude(Q(user_id=None) | Q(user_id__in=settings.STATS_REQUEST_IGNORE_USER_IDS))
.values("user__id", "user__name")
.annotate(total_requests=Count("id", distinct=True), unique_requests=Count("song__id", distinct=True))
.order_by("-unique_requests")[: settings.STATS_TOP_COUNT]
)
stats["total_unique_requests"] = (
PlaylistSong.objects.filter(state=2)
.exclude(Q(user_id=None) | Q(user_id__in=settings.STATS_REQUEST_IGNORE_USER_IDS))
.aggregate(total=Count("song__id", distinct=True))
)
stats["most_played_songs"] = (
PlaylistSong.objects.filter(state=2)
.exclude(Q(user_id=None) | Q(user_id__in=settings.STATS_REQUEST_IGNORE_USER_IDS))
.values("song__artist", "song__title")
.annotate(total=Count("id"))
.order_by("-total", "song__artist")[: settings.STATS_TOP_COUNT]
)
stats["most_played_artists"] = (
PlaylistSong.objects.filter(state=2)
.exclude(Q(user_id=None) | Q(user_id__in=settings.STATS_REQUEST_IGNORE_USER_IDS))
.values("song__artist")
.annotate(total=Count("song__artist"))
.order_by("-total", "song__artist")[: settings.STATS_TOP_COUNT]
)
stats["most_played_songs_14_days"] = (
PlaylistSong.objects.filter(state=2, played_at__gte=timezone.now() - timedelta(days=14))
.exclude(user_id=None)
.values("song__artist", "song__title")
.annotate(total=Count("id"))
.order_by("-total", "song__artist")[: settings.STATS_TOP_COUNT]
)
stats["time_requested"] = (
PlaylistSong.objects.filter(state=2)
.exclude(Q(user_id=None) | Q(user_id__in=settings.STATS_REQUEST_IGNORE_USER_IDS))
.values("user__id", "user__name")
.annotate(total=Sum("song__duration"), avg_dur=Sum("song__duration") / Count("id"))
.order_by("-total")[: settings.STATS_TOP_COUNT]
)
stats["total_time_requested"] = (
PlaylistSong.objects.all()
.filter(state=2)
.exclude(Q(user_id=None) | Q(user_id__in=settings.STATS_REQUEST_IGNORE_USER_IDS))
.aggregate(total=Sum("song__duration"))
)
total_time_overall = sum(x["avg_dur"] for x in list(stats["time_requested"]))
total_average = total_time_overall / len(stats["time_requested"])
avg_dur_min, avg_dur_sec = divmod(total_average, 60)
total_average = "{:.0f} minutes and {:.0f} seconds".format(avg_dur_min, avg_dur_sec)
requests_uploader = (
PlaylistSong.objects.filter(state=2)
.exclude(Q(user_id=None) | Q(user_id__in=settings.STATS_REQUEST_IGNORE_USER_IDS))
.values("song__user__id", "song__user__name", "user__name", "user__id")
.annotate(total=Count("song__user__name"))
)
most_requested_uploaders = []
best_uploaders_list(list(requests_uploader), most_requested_uploaders)
most_requested_uploaders = sorted(most_requested_uploaders, key=lambda x: x["total"], reverse=True)[
: settings.STATS_TOP_COUNT
]
for time in list(stats["time_requested"]):
# converts total time and average time in respectively days and minutes, seconds
time["duration"] = str(round(time["total"] / 86400, 2)) + " days"
avg_dur_min, avg_dur_sec = divmod(time["avg_dur"], 60)
if avg_dur_sec < 10:
avg_dur_sec = "0" + str(avg_dur_sec)
time["avg_dur"] = "{}:{}".format(avg_dur_min, avg_dur_sec)
# Convert requested time to days
time_requested = list(stats["time_requested"])
for tr in time_requested:
tr["duration"] = "{:5.2f} days".format(tr["total"] / 86400)
return {
"last_updated": last_updated,
"total_uploads": "{0:,.0f}".format(stats["total_uploads"]),
"total_uploads_perc": stats["total_uploads"],
"upload_stats": list(stats["upload_stats"]),
"total_requests": "{0:,.0f}".format(stats["total_requests"]),
"total_requests_perc": stats["total_requests"],
"request_stats": list(stats["request_stats"]),
"unique_request_stats": list(stats["unique_request_stats"]),
"total_unique_requests": "{0:,.0f}".format(stats["total_unique_requests"]["total"]),
"most_played_songs": list(stats["most_played_songs"]),
"most_played_artists": list(stats["most_played_artists"]),
"most_played_songs_14_days": list(stats["most_played_songs_14_days"]),
"time_requested": stats["time_requested"],
"total_time_requested": str(round(float(stats["total_time_requested"]["total"]) / 86400, 2)) + " days",
"stats_top_count": settings.STATS_TOP_COUNT,
"most_requested_uploaders": list(most_requested_uploaders),
"total_average": total_average,
}
def user_stats(request):
last_updated = datetime.now()
total_uploads = Song.objects.filter(user__id=request, deleted=False).count()
total_requests = PlaylistSong.objects.filter(user__id=request, state=2).count()
unique_requests = (
PlaylistSong.objects.filter(user__id=request, state=2, song_id__isnull=False)
.values("song_id")
.distinct()
.count()
)
most_played_songs = (
PlaylistSong.objects.filter(user__id=request, state=2)
.exclude(Q(user_id=None) | Q(user_id__in=settings.STATS_REQUEST_IGNORE_USER_IDS))
.values("song__artist", "song__title")
.annotate(total=Count("id"))
.order_by("-total", "song__artist", "song__title")[: settings.STATS_TOP_COUNT]
)
most_played_artists = (
PlaylistSong.objects.filter(user__id=request, state=2, song_id__isnull=False)
.values("song__artist")
.annotate(total=Count("song__artist"))
.order_by("-total", "song__artist")[: settings.STATS_TOP_COUNT]
)
most_played_uploaders = (
PlaylistSong.objects.filter(user__id=request, state=2)
.exclude(Q(user_id=None) | Q(user_id__in=settings.STATS_REQUEST_IGNORE_USER_IDS))
.values("song__user__id", "song__user__name")
.annotate(total=Count("song__user__id"))
.order_by("-total")[: settings.STATS_TOP_COUNT]
)
biggest_fans = (
PlaylistSong.objects.filter(state=2, song_id__in=Song.objects.filter(user__id=request))
.exclude(Q(user_id=None) | Q(user_id__in=settings.STATS_REQUEST_IGNORE_USER_IDS))
.values("user__id", "user__name")
.annotate(total=Count("user__id"))
.order_by("-total")[: settings.STATS_TOP_COUNT]
)
most_played_uploads = (
PlaylistSong.objects.filter(state=2, song_id__in=Song.objects.filter(user__id=request))
.exclude(user__id=None)
.values("song__artist", "song__title")
.annotate(total=Count("id", filter=~Q(user__id=request)), user_total=Count("id", filter=Q(user__id=request)))
.order_by("-total", "song__artist", "song__title")
)
most_played_uploaded_artists = (
PlaylistSong.objects.filter(state=2, song_id__in=Song.objects.filter(user__id=request))
.exclude(user__id=None)
.values("song__artist")
.annotate(
total=Count("song__artist", filter=~Q(user__id=request)),
user_total=Count("id", filter=Q(user__id=request)),
)
.order_by("-total", "song__artist")
)
most_played = list(most_played_uploads)
total_played = {}
total_played["uploads"] = 0
total_played["user_uploads"] = 0
for x in most_played:
total_played["uploads"] += x["total"]
total_played["user_uploads"] += x["user_total"]
most_played_uploads_list = sorted(most_played_uploads, key=lambda x: (x["song__artist"], x["song__title"]))
most_played_uploads_list = sorted(most_played_uploads_list, key=lambda x: x["total"], reverse=True)[
: settings.STATS_TOP_COUNT
]
most_played_uploaded_artists = sorted(list(most_played_uploaded_artists), key=lambda x: x["total"], reverse=True)[
: settings.STATS_TOP_COUNT
]
return {
"last_updated": last_updated,
"total_uploads": total_uploads,
"total_requests": total_requests,
"unique_requests": unique_requests,
"most_played_songs": list(most_played_songs),
"most_played_artists": list(most_played_artists),
"most_played_uploaders": list(most_played_uploaders),
"most_played_uploads": most_played_uploads_list,
"most_played_uploaded_artists": most_played_uploaded_artists,
"stats_top_count": settings.STATS_TOP_COUNT,
"total_played_uploads": total_played["uploads"],
"total_played_user_uploads": total_played["user_uploads"],
"biggest_fans": list(biggest_fans),
}