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), }