Merge branch 'oslomp/MarietjeDjango-improved_stats' into 'marietje-zuid'

Oslomp/marietje django improved stats

See merge request dsprenkels/MarietjeDjango!20
This commit is contained in:
Gerdriaan Mulder
2019-02-15 17:04:13 +01:00
5 changed files with 152 additions and 64 deletions

View File

@ -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'

View File

@ -18,8 +18,8 @@
</div>
<div class="col-md-6">
<h2>Uploads</h2>
<h4>Total: {{ stats.total_uploads }}</h4>
<h4>Top {{ stats.stats_top_count }}:</h4>
<p>In total <strong> {{ stats.total_uploads }} </strong> songs have been uploaded.
These are the {{ stats.stats_top_count }} people who have uploaded the most.</p>
<div class="table-responsive">
<table class="table table-striped">
<thead>
@ -43,8 +43,8 @@
</div>
<div class="col-md-6">
<h2>Requests</h2>
<h4>Total: {{ stats.total_requests }}</h4>
<h4>Top {{ stats.stats_top_count }}:</h4>
<p>In total <strong> {{ stats.total_requests }} </strong> songs have been requested.
These are the {{ stats.stats_top_count }} people who have requested the most songs.</p>
<div class="table-responsive">
<table class="table table-striped">
<thead>
@ -68,15 +68,17 @@
</div>
<div class="col-md-6">
<h2>Time requested</h2>
<h4>Total: {{stats.total_time_requested}}</h4>
<h4>Top {{ stats.stats_top_count }}:</h4>
<p>In total <strong> {{ stats.total_time_requested }} </strong> of music have been requested, with an
average song length of <strong>{{ stats.total_average }}</strong>.
These are the {{ stats.stats_top_count }} people with the longest total time queued</p>
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th>#</th>
<th>User</th>
<th>Duration</th>
<th style="text-align: right;">Duration</th>
<th>Average</th>
</tr>
</thead>
<tbody>
@ -84,7 +86,8 @@
<tr>
<th>{{ forloop.counter }}</th>
<td>{{ stat.user__name }}</td>
<td>{{ stat.duration }}</td>
<td style="text-align: right;">{{ stat.duration }}</td>
<td>{{stat.avg_dur}}</td>
</tr>
{% endfor %}
</tbody>
@ -93,8 +96,9 @@
</div>
<div class="col-md-6">
<h2>Unique requests</h2>
<h4>Total: {{stats.total_unique_requests.total}}</h4>
<h4>Top {{ stats.stats_top_count }}:</h4>
<p>In total <strong> {{stats.total_unique_requests.total}}</strong> different songs
have been requested. The {{ stats.stats_top_count }} people that have requested the largest number of
different songs are shown below.
<div class="table-responsive">
<table class="table table-striped">
<thead>
@ -118,7 +122,7 @@
</div>
<div class="col-md-6">
<h2>Most played songs</h2>
<h4>Top {{ stats.stats_top_count }}:</h4>
<p>These are the {{ stats.stats_top_count }} most played songs ever.</p>
<div class="table-responsive">
<table class="table table-striped">
<thead>
@ -144,22 +148,25 @@
</div>
<div class="col-md-6">
<h2>Most played uploaders</h2>
<h4>Top {{ stats.stats_top_count }}:</h4>
<p>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.</p>
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th>#</th>
<th>User</th>
<th># Songs</th>
<th># Others</th>
<th># Own</th>
</tr>
</thead>
<tbody>
{% for stat in stats.most_requested_uploaders %}
<tr>
<th>{{ forloop.counter }}</th>
<td>{{ stat.song__user__name }}</td>
<td>{{ stat.total }} ({% widthratio stat.total stats.total_requests 100 %}%)</td>
<td>{{ stat.name }}</td>
<td>{{ stat.total }}</td>
<td>{{ stat.own_total}}</td>
</tr>
{% endfor %}
</tbody>
@ -168,7 +175,7 @@
</div>
<div class="col-md-6">
<h2>Most played songs last 14 days</h2>
<h4>Top {{ stats.stats_top_count }}:</h4>
<p>These songs are played the {{ stats.stats_top_count }} most in the last two weeks.</p>
<div class="table-responsive">
<table class="table table-striped">
<thead>

View File

@ -8,17 +8,22 @@
<h1>User Statistics</h1>
<div class="row">
{% if not stats %}
<div class="alert alert-danger">
<strong>Stats unavailable :(</strong>
</div>
{% else %}
{% if current_age_text %}
<div class="alert alert-info">
<strong>{{ current_age_text }}</strong>
{% endif %}
</div>
<h4>Total uploads: {{ stats.total_uploads }}</h4>
<h4>Total requests: {{ stats.total_requests }}</h4>
<h4>Unique requests: {{ stats.unique_requests }} ({% widthratio stats.unique_requests stats.total_requests 100 %}%)</h4>
<h4>Total requested uploads: {{stats.total_played_uploads}}</h4>
</div>
<div class="col-md-6">
<h2>Most played songs</h2>
<p>You have requested <strong> {{ stats.unique_requests }} </strong> different
songs a total of <strong> {{ stats.total_requests }} </strong> times. This
means <strong> {% widthratio stats.unique_requests stats.total_requests 100 %}% </strong> of your requests have been unique. </p>
<h4>Top {{ stats.stats_top_count }}:</h4>
<div class="table-responsive">
<table class="table table-striped">
@ -45,7 +50,12 @@
</div>
<div class="row">
<div class="col-md-6">
<h2>Most played uploads</h2>
<h2>Uploads requested</h2>
<p> You have uploaded a total of <strong> {{stats.total_uploads }} </strong> 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 <strong> {{stats.total_played_uploads }} </strong> times by others and
<strong> {{stats.total_played_user_uploads }} </strong> by yourself.
<h4>Top {{ stats.stats_top_count }}:</h4>
<div class="table-responsive">
<table class="table table-striped">
@ -54,7 +64,8 @@
<th>#</th>
<th>Artist</th>
<th>Title</th>
<th># Requests</th>
<th>Others</th>
<th>You</th>
</tr>
</thead>
<tbody>
@ -64,6 +75,7 @@
<td>{{ stat.song__artist }}</td>
<td>{{ stat.song__title }}</td>
<td>{{ stat.total }}</td>
<td>{{ stat.user_total }}</td>
</tr>
{% endfor %}
</tbody>
@ -72,7 +84,7 @@
</div>
<div class="col-md-6">
<h2>Most played uploaders</h2>
<h4>Top {{ stats.stats_top_count }}:</h4>
<p> The people whose songs you have queued the most are:</p>
<div class="table-responsive">
<table class="table table-striped">
<thead>
@ -87,7 +99,7 @@
<tr>
<th>{{ forloop.counter }}</th>
<td>{{ stat.song__user__name }}</td>
<td>{{ stat.total }} ({% widthratio stat.total total_requests 100 %}%)</td>
<td>{{ stat.total }} ({% widthratio stat.total stats.total_requests 100 %}%)</td>
</tr>
{% endfor %}
</tbody>
@ -95,5 +107,6 @@
</div>
</div>
</div>
{% endif %}
</div>
{% endblock %}

View File

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

View File

@ -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