19 Commits

Author SHA1 Message Date
371334326b quickfix artist stat 2020-03-28 15:12:09 +01:00
40e2245d39 Merge branch 'oslomp/artists' into 'marietje-zuid'
Artist statistics

See merge request dsprenkels/MarietjeDjango!44
2020-03-28 14:26:31 +01:00
c9ba37d291 Artist statistics 2020-03-28 14:26:31 +01:00
6ec3d57a39 Merge branch 'marietje-zuid' into 'marietje-zuid'
Remove python2 compatibility library django.utils.six

See merge request dsprenkels/MarietjeDjango!45
2020-03-28 14:09:16 +01:00
59993aa1c0 Remove unused import ASCIIUsernameValidator 2020-03-28 14:07:00 +01:00
a1116ca4cd Remove python2 compatibility library django.utils.six 2020-03-28 14:01:13 +01:00
95bd47c46a Bump django version 2020-03-04 14:56:04 +01:00
af6031c3a5 Merge branch 'revert-7af71276' into 'marietje-zuid'
Revert "Merge branch 'oslomp/search_field' into 'marietje-zuid'"

See merge request dsprenkels/MarietjeDjango!43
2019-11-01 17:34:22 +01:00
a3f501273f Revert "Merge branch 'oslomp/search_field' into 'marietje-zuid'"
This reverts merge request !42
2019-11-01 17:31:44 +01:00
7af7127612 Merge branch 'oslomp/search_field' into 'marietje-zuid'
Merged both search-fields into one

See merge request dsprenkels/MarietjeDjango!42
2019-10-31 10:32:07 +01:00
4c9a431f8a Merge both search-fields into one 2019-10-31 10:32:07 +01:00
581e14f7ef Merge branch 'dsprenkels/issue_15' into 'marietje-zuid'
Set all cache TTLs to 2 days

Closes #15

See merge request dsprenkels/MarietjeDjango!39
2019-10-25 19:35:59 +02:00
d0ed0a0a62 Set all cache TTLs to 2 days
Fixes #15.
2019-10-25 19:33:12 +02:00
ce3bfb02d5 Merge branch 'cron' into 'marietje-zuid'
Regenerate caches after apply patch

Closes #14

See merge request dsprenkels/MarietjeDjango!38
2019-10-25 19:20:51 +02:00
065f29fe55 gitlab-ci: Regenerate caches after apply patch
This commit fixes #14.
2019-10-25 19:18:54 +02:00
3bdac86bfb Merge branch 'new_stat' into 'marietje-zuid'
Extra statistic: biggest fans

See merge request dsprenkels/MarietjeDjango!37
2019-10-23 21:03:43 +02:00
a864e8f535 Extra statistic: biggest fans 2019-10-23 20:13:12 +02:00
48dd3bd0df Merge branch 'visual_changes' into 'marietje-zuid'
Visual changes

See merge request dsprenkels/MarietjeDjango!36
2019-04-08 18:09:16 +02:00
2fcd827b85 Visual changes 2019-04-08 18:09:14 +02:00
11 changed files with 174 additions and 27 deletions

View File

@ -39,4 +39,10 @@ deploy:
"\$PYTHON" "\$MANAGE" migrate --noinput
"\$PYTHON" "\$MANAGE" collectstatic --noinput
systemctl start MarietjeDjango.service
# Regenerate caches
(
sudo -u www-data /srv/MarietjeDjango/django_env/bin/python /srv/MarietjeDjango/marietje/manage.py recache_stats
sudo -u www-data /srv/MarietjeDjango/django_env/bin/python /srv/MarietjeDjango/marietje/manage.py recache_user_stats
) &
EOF

View File

@ -20,6 +20,7 @@ from prometheus_client import Counter
from marietje.utils import song_to_dict, playlist_song_to_dict, send_to_bertha
from queues.models import PlaylistSong, QueueCommand
from songs.models import Song
from marietje.settings import MAX_MINUTES_IN_A_ROW
request_counter = Counter('marietje_requests', 'Queue requests on marietje', ['queue'])
upload_counter = Counter('marietje_uploads', 'Songs uploaded to marietje')
@ -168,7 +169,7 @@ def managesongs(request):
@api_auth_required
def queue(request):
queue = request.user.queue
infobar = {"start_personal_queue": 0, "length_personal_queue": 0, "length_total_queue": 0, "end_personal_queue": 0}
infobar = {"start_personal_queue": 0, "length_personal_queue": 0, "length_total_queue": 0, "end_personal_queue": 0, 'max_length': MAX_MINUTES_IN_A_ROW}
for song in queue.queue():
infobar["length_total_queue"] += song.song.duration
if song.user == request.user:
@ -177,7 +178,6 @@ def queue(request):
if infobar["start_personal_queue"] == 0:
infobar["start_personal_queue"] = infobar["length_total_queue"] - song.song.duration
json = {
'current_song': playlist_song_to_dict(queue.current_song()),
'queue': [playlist_song_to_dict(playlist_song, user=request.user) for playlist_song in queue.queue()],

View File

@ -1,10 +1,10 @@
from django.contrib.auth.base_user import AbstractBaseUser, BaseUserManager
from django.contrib.auth.hashers import make_password
from django.contrib.auth.models import PermissionsMixin
from django.contrib.auth.validators import ASCIIUsernameValidator, UnicodeUsernameValidator
from django.contrib.auth.validators import UnicodeUsernameValidator
from django.core.mail import send_mail
from django.db import models
from django.utils import six, timezone
from django.utils import timezone
from django.utils.translation import ugettext_lazy as _
from marietje.utils import get_first_queue
@ -46,7 +46,7 @@ class UserManager(BaseUserManager):
class User(AbstractBaseUser, PermissionsMixin):
username_validator = UnicodeUsernameValidator() if six.PY3 else ASCIIUsernameValidator()
username_validator = UnicodeUsernameValidator()
username = models.CharField(
_('username'),

View File

@ -40,8 +40,8 @@ footer {
border-bottom: 1px solid #DDDDDD;
}
.requested_song .plays-at, .requested_song .requested-by {
font-weight: bold;
tr.requested_song{
border-left: 1px solid #777777;
}
.table-header-style td, .table-header-style{

View File

@ -183,6 +183,11 @@ function updateTime() {
}
if (infobar['end_personal_queue'] !== 0){
$('.start-queue').text("First song starts " + showExactOrRelative(infobar['start_personal_queue']));
if (infobar['length_personal_queue'] > infobar['max_length'] * 60) {
$('.duration-queue').addClass('text-danger');
} else {
$('.duration-queue').removeClass('text-danger');
}
$('.duration-queue').text( " (" + (infobar['length_personal_queue']).secondsToMMSS() + ")");
$('.end-queue').text("Last song ends " + showExactOrRelative(infobar['end_personal_queue']));
}

View File

@ -36,9 +36,11 @@
<ul class="nav navbar-nav navbar-right hidden-xs">
<li>
<div class="infobar">
<p class="navbar-text start-queue"></p>
<p class="navbar-text start-queue hidden-sm hidden-xs"></p>
<p class="navbar-text end-queue"></p>
<p class="navbar-text duration-queue"></p>
<div class="navbar-text">
<p class="duration-queue"></p>
</div>
</div>
</li>
</ul>
@ -93,7 +95,9 @@
<td class="col-md-4">Artist</td>
<td class="col-md-4">Title</td>
<td class="col-md-2 hidden-xs">Requested By</td>
<td id="timeswitch" class="col-md-1 hidden-xs text-info" >Plays In</td>
<td class="col-md-1 hidden-xs text-info" style="cursor: pointer;">
<span id="timeswitch" class="btn-link" >Plays In</span>
</td>
<td class="col-md-1 control-icons">Control</td>
</tr>
<tr class="currentsong" style="font-weight: bold">

View File

@ -72,7 +72,7 @@
<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>
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>
@ -148,11 +148,34 @@
</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>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>
<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>

View File

@ -48,7 +48,30 @@
</table>
</div>
</div>
<div class="row">
<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
@ -82,6 +105,34 @@
</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>
@ -107,7 +158,31 @@
</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>
</div>
{% endif %}
</div>
{% endblock %}
{% endblock %}

View File

@ -10,10 +10,12 @@ 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, 2 * 3600)
caches['default'].set('stats', new_stats, CACHE_TTL)
return new_stats
@ -25,7 +27,7 @@ def recache_user_stats():
new_stats = user_stats(user['id'])
cacheloc = 'userstats_{}'.format(user['id'])
caches['userstats'].delete(cacheloc)
caches['userstats'].set(cacheloc, new_stats, 48 * 3600)
caches['userstats'].set(cacheloc, new_stats, CACHE_TTL)
return new_stats
@ -104,6 +106,12 @@ def compute_stats():
'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(
@ -163,6 +171,7 @@ def compute_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(
@ -195,6 +204,12 @@ def user_stats(request):
'-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)
@ -203,31 +218,50 @@ def user_stats(request):
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')
'-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_uploads = 0
total_played_user_uploads = 0
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']
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,
'total_played_uploads': total_played['uploads'],
'total_played_user_uploads': total_played['user_uploads'],
'biggest_fans': list(biggest_fans),
}

View File

@ -1,5 +1,5 @@
setuptools
django==2.1
django>=2.2,<2.3
mysqlclient
https://projects.unbit.it/downloads/uwsgi-lts.tar.gz
mutagen

View File

@ -1,4 +1,4 @@
django
django>=2.2,<2.3
mutagen
argon2-cffi
prometheus_client