Marietje 4.1: Addition of Django REST framework, Swagger, Dark mode and updates to Django and Bootstrap

This commit is contained in:
Lars van Rhijn
2023-09-14 19:55:51 +02:00
parent 379ababcc0
commit d1a1be7e2e
124 changed files with 4835 additions and 3490 deletions

View File

@ -2,4 +2,5 @@ from django.apps import AppConfig
class StatsConfig(AppConfig):
name = 'stats'
name = "stats"
default_auto_field = "django.db.models.BigAutoField"

View File

@ -2,8 +2,9 @@ from django.core.management.base import BaseCommand
from stats.utils import recache_stats
class Command(BaseCommand):
help = 'Update the statistics cache'
help = "Update the statistics cache"
def handle(self, *args, **options):
recache_stats()

View File

@ -2,8 +2,9 @@ from django.core.management.base import BaseCommand
from stats.utils import recache_user_stats
class Command(BaseCommand):
help = 'Update the statistics cache'
help = "Update the statistics cache"
def handle(self, *args, **options):
recache_user_stats()

View File

@ -0,0 +1,13 @@
from datetime import datetime, timedelta
def age_text(last_updated):
current_age = datetime.now() - last_updated
minutes = (current_age.seconds % 3600) / 60
hours = current_age.seconds / 3600
minutestr = "minute" if minutes == 1 else "minutes"
hourstr = "hour" if hours == 1 else "hours"
if current_age < timedelta(hours=1):
return "Stats were updated {:.0f} {} ago.".format(minutes, minutestr)
return "Stats were updated {:.0f} {} and {:.0f} {} ago.".format(hours, hourstr, minutes, minutestr)

View File

@ -1,230 +1,232 @@
{% extends 'base.html' %}
{% extends 'marietje/base.html' %}
{% load static %}
{% block title %}Stats{% endblock %}
{% block content %}
<h1>Statistics</h1>
<div class="row display-flex">
{% if not stats %}
<div class="col-xs-12 alert alert-danger">
<strong>Stats unavailable :(</strong>
</div>
{% else %}
{% if current_age_text %}
<div class="col-xs-12 alert alert-info">
<strong>{{ current_age_text }}</strong>
{% endif %}
</div>
<div class="col-md-6">
<h2>Uploads</h2>
<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>
<tr>
<th>#</th>
<th>User</th>
<th style="text-align: right;"># Songs</th>
</tr>
</thead>
<tbody>
{% for stat in stats.upload_stats %}
<tr>
<th>{{ forloop.counter }}</th>
<td>{{ stat.user__name }}</td>
<td style="text-align: right;">{{ stat.total }}</td>
<td>({% widthratio stat.total stats.total_uploads_perc 100 %}%)</td>
</tr>
{% endfor %}
</tbody>
</table>
<div class="container mt-5">
<h1>Statistics</h1>
<div class="row display-flex">
{% if not stats %}
<div class="col-xs-12 alert alert-danger">
<strong>Stats unavailable :(</strong>
</div>
{% else %}
{% if current_age_text %}
<div class="col-xs-12 alert alert-info">
<strong>{{ current_age_text }}</strong>
{% endif %}
</div>
<div class="col-md-6">
<h2>Uploads</h2>
<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>
<tr>
<th>#</th>
<th>User</th>
<th style="text-align: right;"># Songs</th>
</tr>
</thead>
<tbody>
{% for stat in stats.upload_stats %}
<tr>
<th>{{ forloop.counter }}</th>
<td>{{ stat.user__name }}</td>
<td style="text-align: right;">{{ stat.total }}</td>
<td>({% widthratio stat.total stats.total_uploads_perc 100 %}%)</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
<div class="col-md-6">
<h2>Requests</h2>
<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>
<tr>
<th>#</th>
<th>User</th>
<th style="text-align: right;"># Requests</th>
</tr>
</thead>
<tbody>
{% for stat in stats.request_stats %}
<tr>
<th>{{ forloop.counter }}</th>
<td>{{ stat.user__name }}</td>
<td style="text-align: right;">{{ stat.total }}</td>
<td>({% widthratio stat.total stats.total_requests_perc 100 %}%)</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
<div class="col-md-6">
<h2>Time requested</h2>
<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 style="text-align: right;">Duration</th>
<th>Average</th>
</tr>
</thead>
<tbody>
{% for stat in stats.time_requested %}
<tr>
<th>{{ forloop.counter }}</th>
<td>{{ stat.user__name }}</td>
<td style="text-align: right;">{{ stat.duration }}</td>
<td>{{stat.avg_dur}}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
<div class="col-md-6">
<h2>Unique requests</h2>
<p>In total <strong> {{stats.total_unique_requests}}</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>
<tr>
<th>#</th>
<th>User</th>
<th style="text-align: right;"># Unique</th>
</tr>
</thead>
<tbody>
{% for stat in stats.unique_request_stats %}
<tr>
<th>{{ forloop.counter }}</th>
<td>{{ stat.user__name }}</td>
<td style="text-align: right;">{{ stat.unique_requests }}</td>
<td>({% widthratio stat.unique_requests stat.total_requests 100 %}%)</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
<div class="col-md-6">
<h2>Most played songs</h2>
<p>These are the {{ stats.stats_top_count }} most played songs ever.</p>
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th>#</th>
<th>Artist</th>
<th>Title</th>
<th># Requests</th>
</tr>
</thead>
<tbody>
{% for stat in stats.most_played_songs %}
<tr>
<th>{{ forloop.counter }}</th>
<td>{{ stat.song__artist }}</td>
<td>{{ stat.song__title }}</td>
<td>{{ stat.total }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
<div class="col-md-6">
<h2>Most played Artists</h2>
<p>These are the {{ stats.stats_top_count }} most played artists ever.</p>
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th>#</th>
<th>Artist</th>
<th style="text-align: right;"># Requests</th>
</tr>
</thead>
<tbody>
{% for stat in stats.most_played_artists %}
<tr>
<th>{{ forloop.counter }}</th>
<td>{{ stat.song__artist }}</td>
<td style="text-align: right;">{{ stat.total }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
<div class="col-md-6">
<h2>Most played uploaders</h2>
<p>These are the {{ stats.stats_top_count }} people whose songs are requested most often by other people, as shown in the left column. 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 style="text-align: right;"># Others</th>
<th># Own</th>
</tr>
</thead>
<tbody>
{% for stat in stats.most_requested_uploaders %}
<tr>
<th>{{ forloop.counter }}</th>
<td>{{ stat.name }}</td>
<td style="text-align: right;">{{ stat.total }}</td>
<td>{{ stat.own_total}}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
<div class="col-md-6">
<h2>Most played songs last 14 days</h2>
<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>
<tr>
<th>#</th>
<th>Artist</th>
<th>Title</th>
<th># Requests</th>
</tr>
</thead>
<tbody>
{% for stat in stats.most_played_songs_14_days %}
<tr>
<th>{{ forloop.counter }}</th>
<td>{{ stat.song__artist }}</td>
<td>{{ stat.song__title }}</td>
<td>{{ stat.total }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endif %}
</div>
</div>
<div class="col-md-6">
<h2>Requests</h2>
<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>
<tr>
<th>#</th>
<th>User</th>
<th style="text-align: right;"># Requests</th>
</tr>
</thead>
<tbody>
{% for stat in stats.request_stats %}
<tr>
<th>{{ forloop.counter }}</th>
<td>{{ stat.user__name }}</td>
<td style="text-align: right;">{{ stat.total }}</td>
<td>({% widthratio stat.total stats.total_requests_perc 100 %}%)</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
<div class="col-md-6">
<h2>Time requested</h2>
<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 style="text-align: right;">Duration</th>
<th>Average</th>
</tr>
</thead>
<tbody>
{% for stat in stats.time_requested %}
<tr>
<th>{{ forloop.counter }}</th>
<td>{{ stat.user__name }}</td>
<td style="text-align: right;">{{ stat.duration }}</td>
<td>{{stat.avg_dur}}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
<div class="col-md-6">
<h2>Unique requests</h2>
<p>In total <strong> {{stats.total_unique_requests}}</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>
<tr>
<th>#</th>
<th>User</th>
<th style="text-align: right;"># Unique</th>
</tr>
</thead>
<tbody>
{% for stat in stats.unique_request_stats %}
<tr>
<th>{{ forloop.counter }}</th>
<td>{{ stat.user__name }}</td>
<td style="text-align: right;">{{ stat.unique_requests }}</td>
<td>({% widthratio stat.unique_requests stat.total_requests 100 %}%)</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
<div class="col-md-6">
<h2>Most played songs</h2>
<p>These are the {{ stats.stats_top_count }} most played songs ever.</p>
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th>#</th>
<th>Artist</th>
<th>Title</th>
<th># Requests</th>
</tr>
</thead>
<tbody>
{% for stat in stats.most_played_songs %}
<tr>
<th>{{ forloop.counter }}</th>
<td>{{ stat.song__artist }}</td>
<td>{{ stat.song__title }}</td>
<td>{{ stat.total }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
<div class="col-md-6">
<h2>Most played Artists</h2>
<p>These are the {{ stats.stats_top_count }} most played artists ever.</p>
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th>#</th>
<th>Artist</th>
<th style="text-align: right;"># Requests</th>
</tr>
</thead>
<tbody>
{% for stat in stats.most_played_artists %}
<tr>
<th>{{ forloop.counter }}</th>
<td>{{ stat.song__artist }}</td>
<td style="text-align: right;">{{ stat.total }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
<div class="col-md-6">
<h2>Most played uploaders</h2>
<p>These are the {{ stats.stats_top_count }} people whose songs are requested most often by other people, as shown in the left column. 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 style="text-align: right;"># Others</th>
<th># Own</th>
</tr>
</thead>
<tbody>
{% for stat in stats.most_requested_uploaders %}
<tr>
<th>{{ forloop.counter }}</th>
<td>{{ stat.name }}</td>
<td style="text-align: right;">{{ stat.total }}</td>
<td>{{ stat.own_total}}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
<div class="col-md-6">
<h2>Most played songs last 14 days</h2>
<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>
<tr>
<th>#</th>
<th>Artist</th>
<th>Title</th>
<th># Requests</th>
</tr>
</thead>
<tbody>
{% for stat in stats.most_played_songs_14_days %}
<tr>
<th>{{ forloop.counter }}</th>
<td>{{ stat.song__artist }}</td>
<td>{{ stat.song__title }}</td>
<td>{{ stat.total }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endif %}
</div>
{% endblock %}

View File

@ -1,186 +1,188 @@
{% extends 'base.html' %}
{% extends 'marietje/base.html' %}
{% load static %}
{% load tz %}
{% block title %}User Stats{% endblock %}
{% block content %}
<h1>User Statistics</h1>
<div class="container mt-5">
<h1>User Statistics</h1>
<div class="row display-flex">
{% if not stats %}
<div class="col-xs-12 alert alert-danger">
<strong>Stats unavailable :(</strong>
</div>
{% else %}
{% if current_age_text %}
<div class="col-xs-12 alert alert-info">
<strong>{{ current_age_text }}</strong>
</div>
{% endif %}
<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">
<thead>
<tr>
<th>#</th>
<th>Artist</th>
<th>Title</th>
<th># Requests</th>
</tr>
</thead>
<tbody>
{% for stat in stats.most_played_songs %}
<tr>
<th>{{ forloop.counter }}</th>
<td>{{ stat.song__artist }}</td>
<td>{{ stat.song__title }}</td>
<td style="text-align: middle;">{{ stat.total }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<div class="row display-flex">
{% if not stats %}
<div class="col-xs-12 alert alert-danger">
<strong>Stats unavailable :(</strong>
</div>
{% else %}
{% if current_age_text %}
<div class="col-xs-12 alert alert-info">
<strong>{{ current_age_text }}</strong>
</div>
{% endif %}
<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">
<thead>
<tr>
<th>#</th>
<th>Artist</th>
<th>Title</th>
<th># Requests</th>
</tr>
</thead>
<tbody>
{% for stat in stats.most_played_songs %}
<tr>
<th>{{ forloop.counter }}</th>
<td>{{ stat.song__artist }}</td>
<td>{{ stat.song__title }}</td>
<td style="text-align: middle;">{{ stat.total }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
<div class="col-md-6">
<h2>Most played Artists</h2>
<h4>Top {{ stats.stats_top_count }}:</h4>
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th>#</th>
<th>Artist</th>
<th style="text-align: right;"># Requests</th>
</tr>
</thead>
<tbody>
{% for stat in stats.most_played_artists %}
<tr>
<th>{{ forloop.counter }}</th>
<td>{{ stat.song__artist }}</td>
<td style="text-align: right;">{{ stat.total }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
<div class="col-md-6">
<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">
<thead>
<tr>
<th>#</th>
<th>Artist</th>
<th>Title</th>
<th style="text-align: right;">Others</th>
<th>You</th>
</tr>
</thead>
<tbody>
{% for stat in stats.most_played_uploads %}
<tr>
<th>{{ forloop.counter }}</th>
<td>{{ stat.song__artist }}</td>
<td>{{ stat.song__title }}</td>
<td style="text-align: right;">{{ stat.total }}</td>
<td>{{ stat.user_total }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
<div class="col-md-6">
<h2>Upload artists requested</h2>
<p> The left column shows how many times songs from artists uploaded by you have been requested by
other people. The right column shows how many times you requested those songs.
<h4>Top {{ stats.stats_top_count }}:</h4>
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th>#</th>
<th>Artist</th>
<th style="text-align: right;">Others</th>
<th>You</th>
</tr>
</thead>
<tbody>
{% for stat in stats.most_played_uploaded_artists %}
<tr>
<th>{{ forloop.counter }}</th>
<td>{{ stat.song__artist }}</td>
<td style="text-align: right;">{{ stat.total }}</td>
<td>{{ stat.user_total }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
<div class="col-md-6">
<h2>Most played uploaders</h2>
<p> The people whose songs you have queued the most are:</p>
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th>#</th>
<th>Uploader</th>
<th style="text-align: right;"># Requests</th>
</tr>
</thead>
<tbody>
{% for stat in stats.most_played_uploaders %}
<tr>
<th>{{ forloop.counter }}</th>
<td>{{ stat.song__user__name }}</td>
<td style="text-align: right;">{{ stat.total }}</td>
<td>({% widthratio stat.total stats.total_requests 100 %}%)</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
<div class="col-md-6">
<h2>Biggest fans</h2>
<p> The people that queued your songs the most are:</p>
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th>#</th>
<th>User</th>
<th style="text-align: right;"># Requests</th>
</tr>
</thead>
<tbody>
{% for stat in stats.biggest_fans %}
<tr>
<th>{{ forloop.counter }}</th>
<td>{{ stat.user__name }}</td>
<td style="text-align: right;">{{ stat.total }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endif %}
</div>
</div>
<div class="col-md-6">
<h2>Most played Artists</h2>
<h4>Top {{ stats.stats_top_count }}:</h4>
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th>#</th>
<th>Artist</th>
<th style="text-align: right;"># Requests</th>
</tr>
</thead>
<tbody>
{% for stat in stats.most_played_artists %}
<tr>
<th>{{ forloop.counter }}</th>
<td>{{ stat.song__artist }}</td>
<td style="text-align: right;">{{ stat.total }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
<div class="col-md-6">
<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">
<thead>
<tr>
<th>#</th>
<th>Artist</th>
<th>Title</th>
<th style="text-align: right;">Others</th>
<th>You</th>
</tr>
</thead>
<tbody>
{% for stat in stats.most_played_uploads %}
<tr>
<th>{{ forloop.counter }}</th>
<td>{{ stat.song__artist }}</td>
<td>{{ stat.song__title }}</td>
<td style="text-align: right;">{{ stat.total }}</td>
<td>{{ stat.user_total }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
<div class="col-md-6">
<h2>Upload artists requested</h2>
<p> The left column shows how many times songs from artists uploaded by you have been requested by
other people. The right column shows how many times you requested those songs.
<h4>Top {{ stats.stats_top_count }}:</h4>
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th>#</th>
<th>Artist</th>
<th style="text-align: right;">Others</th>
<th>You</th>
</tr>
</thead>
<tbody>
{% for stat in stats.most_played_uploaded_artists %}
<tr>
<th>{{ forloop.counter }}</th>
<td>{{ stat.song__artist }}</td>
<td style="text-align: right;">{{ stat.total }}</td>
<td>{{ stat.user_total }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
<div class="col-md-6">
<h2>Most played uploaders</h2>
<p> The people whose songs you have queued the most are:</p>
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th>#</th>
<th>Uploader</th>
<th style="text-align: right;"># Requests</th>
</tr>
</thead>
<tbody>
{% for stat in stats.most_played_uploaders %}
<tr>
<th>{{ forloop.counter }}</th>
<td>{{ stat.song__user__name }}</td>
<td style="text-align: right;">{{ stat.total }}</td>
<td>({% widthratio stat.total stats.total_requests 100 %}%)</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
<div class="col-md-6">
<h2>Biggest fans</h2>
<p> The people that queued your songs the most are:</p>
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th>#</th>
<th>User</th>
<th style="text-align: right;"># Requests</th>
</tr>
</thead>
<tbody>
{% for stat in stats.biggest_fans %}
<tr>
<th>{{ forloop.counter }}</th>
<td>{{ stat.user__name }}</td>
<td style="text-align: right;">{{ stat.total }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endif %}
</div>
{% endblock %}

View File

@ -1,10 +1,10 @@
from django.conf.urls import url
from django.urls import path
from . import views
app_name = 'stats'
app_name = "stats"
urlpatterns = [
url(r'^$', views.stats, name='stats'),
url(r'^user$', views.user_stats, name='user_stats'),
path("", views.StatsView.as_view(), name="stats"),
path("user/", views.UserStatsView.as_view(), name="user_stats"),
]

View File

@ -12,22 +12,21 @@ 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)
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')
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)
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
@ -38,29 +37,24 @@ def best_uploaders_list(requests_uploader, most_requested_uploaders):
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']
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']
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']
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']
most_requested_list[-1]["total"] = requests["total"]
def compute_stats():
@ -68,200 +62,224 @@ def compute_stats():
last_updated = datetime.now()
stats = {}
stats['total_uploads'] = Song.objects.filter(deleted=False).exclude(
user_id=None).count()
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["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["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["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["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["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_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_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["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["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'))
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'])
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)
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'))
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']):
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)
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)
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'])
time_requested = list(stats["time_requested"])
for tr in time_requested:
tr['duration'] = "{:5.2f} days".format(tr['total'] / 86400)
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,
"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_uploads = Song.objects.filter(user__id=request, deleted=False).count()
total_requests = PlaylistSong.objects.filter(
user__id=request, state=2).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()
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_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_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]
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]
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_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_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
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]
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),
"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),
}

View File

@ -1,44 +1,40 @@
from datetime import datetime, timedelta
from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic import TemplateView
from django.core.cache import caches
from django.shortcuts import render
from stats.services import age_text
def stats(request):
stats_data = caches['default'].get('stats')
status = 503
current_age = None
current_age_text = None
if stats_data:
status = 200
current_age_text = age_text(stats_data['last_updated'])
class StatsView(LoginRequiredMixin, TemplateView):
template_name = "stats/stats.html"
data = {'stats': stats_data, 'current_age': current_age, 'current_age_text': current_age_text}
return render(request, 'stats/stats.html', data, status=status)
def get(self, request, **kwargs):
stats_data = caches["default"].get("stats")
status = 503
current_age = None
current_age_text = None
def user_stats(request):
if stats_data:
status = 200
current_age_text = age_text(stats_data["last_updated"])
stats_data = caches['default'].get('userstats_{}'.format(request.user.id))
status = 503
current_age = None
current_age_text = None
data = {"stats": stats_data, "current_age": current_age, "current_age_text": current_age_text}
return render(request, self.template_name, data, status=status)
if stats_data:
status = 200
current_age_text = age_text(stats_data['last_updated'])
data = {'stats': stats_data, 'current_age': current_age, 'current_age_text': current_age_text}
return render(request, 'stats/user.html', data, status=status)
class UserStatsView(LoginRequiredMixin, TemplateView):
template_name = "stats/user.html"
def age_text(last_updated):
current_age = datetime.now() - last_updated
minutes = (current_age.seconds % 3600) / 60
hours = current_age.seconds / 3600
minutestr = "minute" if minutes == 1 else "minutes"
hourstr = "hour" if hours == 1 else "hours"
if current_age < timedelta(hours=1):
return 'Stats were updated {:.0f} {} ago.'.format(minutes, minutestr)
def get(self, request, **kwargs):
stats_data = caches["default"].get("userstats_{}".format(request.user.id))
status = 503
current_age = None
current_age_text = None
return 'Stats were updated {:.0f} {} and {:.0f} {} ago.'.format(
hours, hourstr, minutes, minutestr)
if stats_data:
status = 200
current_age_text = age_text(stats_data["last_updated"])
data = {"stats": stats_data, "current_age": current_age, "current_age_text": current_age_text}
return render(request, self.template_name, data, status=status)