diff --git a/marietje/marietje/settings.py b/marietje/marietje/settings.py index 0f4fb17..4745f32 100644 --- a/marietje/marietje/settings.py +++ b/marietje/marietje/settings.py @@ -102,6 +102,11 @@ CACHES = { 'LOCATION': '/var/tmp/MarietjeDjango_cache/song_search', 'OPTIONS': {'MAX_ENTRIES': 1000}, }, + 'userstats': { + 'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache', + 'LOCATION': '/var/tmp/MarietjeDjango_cache/default', + 'OPTIONS': {'MAX_ENTRIES': 1500}, + }, } AUTH_USER_MODEL = 'marietje.User' diff --git a/marietje/stats/templates/stats/stats.html b/marietje/stats/templates/stats/stats.html index 4262d7e..f6ed1f6 100644 --- a/marietje/stats/templates/stats/stats.html +++ b/marietje/stats/templates/stats/stats.html @@ -18,8 +18,8 @@

Uploads

-

Total: {{ stats.total_uploads }}

-

Top {{ stats.stats_top_count }}:

+

In total {{ stats.total_uploads }} songs have been uploaded. + These are the {{ stats.stats_top_count }} people who have uploaded the most.

@@ -43,8 +43,8 @@

Requests

-

Total: {{ stats.total_requests }}

-

Top {{ stats.stats_top_count }}:

+

In total {{ stats.total_requests }} songs have been requested. + These are the {{ stats.stats_top_count }} people who have requested the most songs.

@@ -68,15 +68,17 @@

Time requested

-

Total: {{stats.total_time_requested}}

-

Top {{ stats.stats_top_count }}:

+

In total {{ stats.total_time_requested }} of music have been requested, with an + average song length of {{ stats.total_average }}. + These are the {{ stats.stats_top_count }} people with the longest total time queued

- + + @@ -84,7 +86,8 @@ - + + {% endfor %} @@ -93,8 +96,9 @@

Unique requests

-

Total: {{stats.total_unique_requests.total}}

-

Top {{ stats.stats_top_count }}:

+

In total {{stats.total_unique_requests.total}} different songs + have been requested. The {{ stats.stats_top_count }} people that have requested the largest number of + different songs are shown below.

# UserDurationDurationAverage
{{ forloop.counter }} {{ stat.user__name }}{{ stat.duration }}{{ stat.duration }}{{stat.avg_dur}}
@@ -118,7 +122,7 @@

Most played songs

-

Top {{ stats.stats_top_count }}:

+

These are the {{ stats.stats_top_count }} most played songs ever.

@@ -144,22 +148,25 @@

Most played uploaders

-

Top {{ stats.stats_top_count }}:

+

The left column shows the {{ stats.stats_top_count }} people whose songs are requested most often by other people + people. The right column shows how many times that person has queued his own songs.

- + + {% for stat in stats.most_requested_uploaders %} - - + + + {% endfor %} @@ -168,7 +175,7 @@

Most played songs last 14 days

-

Top {{ stats.stats_top_count }}:

+

These songs are played the {{ stats.stats_top_count }} most in the last two weeks.

# User# Songs# Others# Own
{{ forloop.counter }}{{ stat.song__user__name }}{{ stat.total }} ({% widthratio stat.total stats.total_requests 100 %}%){{ stat.name }}{{ stat.total }}{{ stat.own_total}}
diff --git a/marietje/stats/templates/stats/user.html b/marietje/stats/templates/stats/user.html index 9f23409..0b373a5 100644 --- a/marietje/stats/templates/stats/user.html +++ b/marietje/stats/templates/stats/user.html @@ -8,17 +8,22 @@

User Statistics

+ {% if not stats %} +
+ Stats unavailable :( +
+ {% else %} {% if current_age_text %}
{{ current_age_text }} {% endif %}
-

Total uploads: {{ stats.total_uploads }}

-

Total requests: {{ stats.total_requests }}

-

Unique requests: {{ stats.unique_requests }} ({% widthratio stats.unique_requests stats.total_requests 100 %}%)

-

Total requested uploads: {{stats.total_played_uploads}}

+

Most played songs

+

You have requested {{ stats.unique_requests }} different + songs a total of {{ stats.total_requests }} times. This + means {% widthratio stats.unique_requests stats.total_requests 100 %}% of your requests have been unique.

Top {{ stats.stats_top_count }}:

@@ -45,7 +50,12 @@
-

Most played uploads

+

Uploads requested

+

You have uploaded a total of {{stats.total_uploads }} songs. The left column + shows how many times these have been requested by other people. The right column shows + how many times you requested your own songs. In total your songs + have been queued {{stats.total_played_uploads }} times by others and + {{stats.total_played_user_uploads }} by yourself.

Top {{ stats.stats_top_count }}:

@@ -54,7 +64,8 @@ - + + @@ -64,6 +75,7 @@ + {% endfor %} @@ -72,7 +84,7 @@

Most played uploaders

-

Top {{ stats.stats_top_count }}:

+

The people whose songs you have queued the most are:

# Artist Title# RequestsOthersYou
{{ stat.song__artist }} {{ stat.song__title }} {{ stat.total }}{{ stat.user_total }}
@@ -87,7 +99,7 @@ - + {% endfor %} @@ -95,5 +107,6 @@ + {% endif %} {% endblock %} \ No newline at end of file diff --git a/marietje/stats/utils.py b/marietje/stats/utils.py index a1dec2b..5555ea1 100644 --- a/marietje/stats/utils.py +++ b/marietje/stats/utils.py @@ -16,103 +16,161 @@ def recache_stats(): caches['default'].set('stats', new_stats, 2 * 3600) 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') + 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['default'].delete(cacheloc) - caches['default'].set(cacheloc, new_stats, 48 * 3600) + caches['userstats'].delete(cacheloc) + caches['userstats'].set(cacheloc, new_stats, 48 * 3600) 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 = {} - total_uploads = Song.objects.filter(deleted=False).exclude( + stats['total_uploads'] = Song.objects.filter(deleted=False).exclude( user_id=None).count() - upload_stats = Song.objects.filter(deleted=False).exclude( + 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] - total_requests = PlaylistSong.objects.filter(state=2).exclude( + stats['total_requests'] = PlaylistSong.objects.filter(state=2).exclude( Q(user_id=None) | Q(user_id__in=settings.STATS_REQUEST_IGNORE_USER_IDS)).count() - request_stats = PlaylistSong.objects.filter(state=2).exclude( + 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] - unique_request_stats = PlaylistSong.objects.filter(state=2).exclude( + 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( - '-total_requests')[:settings.STATS_TOP_COUNT] + '-unique_requests')[:settings.STATS_TOP_COUNT] - total_unique_requests = PlaylistSong.objects.filter(state=2).exclude( + 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)) - most_played_songs = PlaylistSong.objects.filter(state=2).exclude( + 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] - most_played_songs_14_days = PlaylistSong.objects.filter( + 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] - time_requested = PlaylistSong.objects.filter(state=2).exclude( + 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')).order_by( - '-total')[:settings.STATS_TOP_COUNT] + 'user__id', 'user__name').annotate( + total=Sum('song__duration'), + avg_dur=Sum('song__duration') / + Count('id')).order_by('-total')[:settings.STATS_TOP_COUNT] - total_time_requested = PlaylistSong.objects.all().filter(state=2).exclude( + 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')) - most_requested_uploaders = PlaylistSong.objects.filter(state=2).exclude( + 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 = '{} minutes and {} 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').annotate(total=Count( - 'song__user__id')).order_by('-total')[:settings.STATS_TOP_COUNT] + '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) + + 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(time_requested) + time_requested = list(stats['time_requested']) for tr in time_requested: tr['duration'] = str(round(tr['total'] / 86400, 2)) + ' days' return { 'last_updated': last_updated, - 'total_uploads': total_uploads, - 'upload_stats': list(upload_stats), - 'total_requests': total_requests, - 'request_stats': list(request_stats), - 'unique_request_stats': list(unique_request_stats), - 'total_unique_requests': total_unique_requests, - 'most_played_songs': list(most_played_songs), - 'most_played_songs_14_days': list(most_played_songs_14_days), - 'time_requested': time_requested, - 'total_time_requested': str(round(float(total_time_requested['total']) / 86400, 2)) + ' days', + 'total_uploads': stats['total_uploads'], + 'upload_stats': list(stats['upload_stats']), + 'total_requests': stats['total_requests'], + 'request_stats': list(stats['request_stats']), + 'unique_request_stats': list(stats['unique_request_stats']), + 'total_unique_requests': stats['total_unique_requests'], + 'most_played_songs': list(stats['most_played_songs']), + '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() @@ -145,15 +203,18 @@ def user_stats(request): most_played_uploads = 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( - 'pk').values( - 'song__artist', - 'song__title').annotate(total=Count('id')).order_by( - '-total', 'song__artist', - 'song__title')[:settings.STATS_TOP_COUNT] + 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')[:settings.STATS_TOP_COUNT] - total_played_uploads = most_played_uploads.aggregate(newtotal=Sum('total')) + most_played = list(most_played_uploads) + 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'] return { 'last_updated': last_updated, @@ -164,5 +225,6 @@ def user_stats(request): 'most_played_uploaders': list(most_played_uploaders), 'most_played_uploads': list(most_played_uploads), 'stats_top_count': settings.STATS_TOP_COUNT, - 'total_played_uploads': total_played_uploads['newtotal'], + 'total_played_uploads': total_played_uploads, + 'total_played_user_uploads': total_played_user_uploads, } diff --git a/marietje/stats/views.py b/marietje/stats/views.py index 9f02eff..9e1a3c6 100644 --- a/marietje/stats/views.py +++ b/marietje/stats/views.py @@ -18,6 +18,7 @@ def stats(request): return render(request, 'stats/stats.html', data, status=status) def user_stats(request): + stats_data = caches['default'].get('userstats_{}'.format(request.user.id)) status = 503 current_age = None
{{ forloop.counter }} {{ stat.song__user__name }}{{ stat.total }} ({% widthratio stat.total total_requests 100 %}%){{ stat.total }} ({% widthratio stat.total stats.total_requests 100 %}%)