Merged branch history_model.
This commit is contained in:
commit
50b4bcc36e
@ -3,6 +3,5 @@ django
|
|||||||
django-debug-toolbar
|
django-debug-toolbar
|
||||||
requests
|
requests
|
||||||
beautifulsoup4
|
beautifulsoup4
|
||||||
redis
|
|
||||||
gunicorn
|
gunicorn
|
||||||
fabric
|
fabric
|
||||||
|
|||||||
@ -1,12 +1,10 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
|
|
||||||
import re
|
import re
|
||||||
import time
|
|
||||||
|
|
||||||
from django.core.management.base import BaseCommand
|
from django.core.management.base import BaseCommand
|
||||||
from django.utils import timezone
|
|
||||||
|
|
||||||
from gameservers.models import Server, PlayerHistory
|
from gameservers.models import Server, ServerHistory
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
from bs4 import BeautifulSoup
|
from bs4 import BeautifulSoup
|
||||||
@ -88,15 +86,14 @@ class ServerParser(object):
|
|||||||
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
help = 'Update population stats for all ss13 servers.'
|
help = 'Update history stats for all ss13 servers.'
|
||||||
|
|
||||||
def handle(self, *args, **kwargs):
|
def handle(self, *args, **kwargs):
|
||||||
parser = ServerParser()
|
parser = ServerParser()
|
||||||
#parser.url = './dump.html' # Use a local file instead when testing
|
#parser.url = './dump.html' # Use a local file instead when testing
|
||||||
servers = parser.run()
|
servers = parser.run()
|
||||||
history = PlayerHistory()
|
|
||||||
now = time.mktime(timezone.now().timetuple())
|
|
||||||
servers_handled = []
|
servers_handled = []
|
||||||
|
new_items = []
|
||||||
|
|
||||||
for data in servers:
|
for data in servers:
|
||||||
# Prevent empty servers with identical names to other, active servers
|
# Prevent empty servers with identical names to other, active servers
|
||||||
@ -106,10 +103,6 @@ class Command(BaseCommand):
|
|||||||
else:
|
else:
|
||||||
servers_handled.append(data['title'])
|
servers_handled.append(data['title'])
|
||||||
|
|
||||||
# Keep the amount of data down in redis
|
|
||||||
history.trim_points(data['title'])
|
|
||||||
|
|
||||||
# TODO: do bulk insert instead!
|
|
||||||
server, created = Server.objects.update_or_create(
|
server, created = Server.objects.update_or_create(
|
||||||
title=data['title'],
|
title=data['title'],
|
||||||
defaults= dict(
|
defaults= dict(
|
||||||
@ -119,8 +112,9 @@ class Command(BaseCommand):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
# Update the player history
|
history = ServerHistory(server=server, players=data['player_count'])
|
||||||
history.add_point(server, now, data['player_count'])
|
new_items.append(history)
|
||||||
|
|
||||||
|
ServerHistory.objects.bulk_create(new_items)
|
||||||
Server.remove_old_servers()
|
Server.remove_old_servers()
|
||||||
|
|
||||||
28
src/gameservers/migrations/0011_serverhistory.py
Normal file
28
src/gameservers/migrations/0011_serverhistory.py
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import models, migrations
|
||||||
|
import django.utils.timezone
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('gameservers', '0010_auto_20150223_1927'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='ServerHistory',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||||
|
('created', models.DateTimeField(default=django.utils.timezone.now)),
|
||||||
|
('players', models.PositiveIntegerField(default=0)),
|
||||||
|
('server', models.ForeignKey(to='gameservers.Server')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'ordering': ['-created', 'server'],
|
||||||
|
},
|
||||||
|
bases=(models.Model,),
|
||||||
|
),
|
||||||
|
]
|
||||||
@ -2,8 +2,6 @@
|
|||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
|
||||||
import redis
|
|
||||||
|
|
||||||
|
|
||||||
class Server(models.Model):
|
class Server(models.Model):
|
||||||
title = models.CharField(max_length=255)
|
title = models.CharField(max_length=255)
|
||||||
@ -28,32 +26,14 @@ class Server(models.Model):
|
|||||||
server.delete()
|
server.delete()
|
||||||
|
|
||||||
|
|
||||||
class PlayerHistory(object):
|
class ServerHistory(models.Model):
|
||||||
def __init__(self, redis_settings=dict(host='localhost', port=6379, db=0)):
|
server = models.ForeignKey(Server)
|
||||||
self.redis = redis.StrictRedis(**redis_settings)
|
created = models.DateTimeField(default=timezone.now)
|
||||||
|
players = models.PositiveIntegerField(default=0)
|
||||||
|
|
||||||
# 32256 = 4 times per hour * 24 hours * 7 days * 4 weeks * 12 months
|
class Meta:
|
||||||
self.max_items = 32256
|
ordering = ['-created', 'server']
|
||||||
|
|
||||||
def add_point(self, server, time, players):
|
def __str__(self):
|
||||||
'''Add a new point in the player history.'''
|
return 'History for {} at {}.'.format(self.server, self.created)
|
||||||
self.redis.lpush(server, '{},{}'.format(time, players))
|
|
||||||
|
|
||||||
def trim_points(self, server):
|
|
||||||
'''Trim away too old points and servers in the player history.'''
|
|
||||||
self.redis.ltrim(server, 0, self.max_items)
|
|
||||||
# let the list expire after a week without updates
|
|
||||||
self.redis.expire(server, 604800)
|
|
||||||
|
|
||||||
def get_history(self, server, days=7):
|
|
||||||
'''Get a range of days in a server's player history.'''
|
|
||||||
# 96 = 4 times per hour * 24 hours
|
|
||||||
max_items = days * 96
|
|
||||||
|
|
||||||
items = []
|
|
||||||
for tmp in self.redis.lrange(server, 0, max_items):
|
|
||||||
time, players = tmp.split(',')
|
|
||||||
time, players = float(time), int(players)
|
|
||||||
items.append((time, players))
|
|
||||||
return items
|
|
||||||
|
|
||||||
|
|||||||
@ -1,8 +1,11 @@
|
|||||||
|
|
||||||
|
from datetime import timedelta
|
||||||
|
|
||||||
from django.shortcuts import render
|
from django.shortcuts import render
|
||||||
from django.views import generic
|
from django.views import generic
|
||||||
|
from django.utils import timezone
|
||||||
|
|
||||||
from .models import Server, PlayerHistory
|
from .models import Server, ServerHistory
|
||||||
|
|
||||||
class ServerListView(generic.ListView):
|
class ServerListView(generic.ListView):
|
||||||
model = Server
|
model = Server
|
||||||
@ -13,25 +16,24 @@ class ServerDetailView(generic.DetailView):
|
|||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super(ServerDetailView, self).get_context_data(**kwargs)
|
context = super(ServerDetailView, self).get_context_data(**kwargs)
|
||||||
server = context['server']
|
server = context['server']
|
||||||
history = PlayerHistory()
|
|
||||||
items = history.get_history(server)
|
weekly_history = ServerHistory.objects.filter(
|
||||||
context['player_history'] = items
|
server=server,
|
||||||
|
created__gte=timezone.now() - timedelta(days=7),
|
||||||
|
)
|
||||||
|
context['weekly_history'] = weekly_history
|
||||||
|
|
||||||
# Moving average for the last day
|
# Moving average for the last day
|
||||||
# TODO: remove the hardcoded value
|
tmp = [tmp.players for tmp in ServerHistory.objects.filter(
|
||||||
tmp = [players for time, players in items[-96:]]
|
server=server,
|
||||||
|
created__gte=timezone.now() - timedelta(days=1))]
|
||||||
context['daily_average'] = sum(tmp) / float(len(tmp))
|
context['daily_average'] = sum(tmp) / float(len(tmp))
|
||||||
context['daily_min'] = min(tmp)
|
context['daily_min'] = min(tmp)
|
||||||
context['daily_max'] = max(tmp)
|
context['daily_max'] = max(tmp)
|
||||||
|
|
||||||
tmp = [players for time, players in items[-96*7:]]
|
tmp = [tmp.players for tmp in weekly_history]
|
||||||
context['weekly_average'] = sum(tmp) / float(len(tmp))
|
context['weekly_average'] = sum(tmp) / float(len(tmp))
|
||||||
context['weekly_min'] = min(tmp)
|
context['weekly_min'] = min(tmp)
|
||||||
context['weekly_max'] = max(tmp)
|
context['weekly_max'] = max(tmp)
|
||||||
|
|
||||||
tmp = [players for time, players in items]
|
|
||||||
context['total_average'] = sum(tmp) / float(len(tmp))
|
|
||||||
context['total_min'] = min(tmp)
|
|
||||||
context['total_max'] = max(tmp)
|
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|||||||
@ -45,12 +45,6 @@
|
|||||||
<th>{{weekly_min}}</th>
|
<th>{{weekly_min}}</th>
|
||||||
<th>{{weekly_max}}</th>
|
<th>{{weekly_max}}</th>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
|
||||||
<th>Total</th>
|
|
||||||
<th>{{total_average|floatformat}}</th>
|
|
||||||
<th>{{total_min}}</th>
|
|
||||||
<th>{{total_max}}</th>
|
|
||||||
</tr>
|
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<div id="chart"></div>
|
<div id="chart"></div>
|
||||||
@ -62,8 +56,8 @@
|
|||||||
$(function() {
|
$(function() {
|
||||||
var series = [
|
var series = [
|
||||||
{# convert timestamp to ms, because javascript... #}
|
{# convert timestamp to ms, because javascript... #}
|
||||||
{% for timestamp, players in player_history %}
|
{% for item in weekly_history %}
|
||||||
[{{timestamp}} * 1000, {{players}}],
|
[{{item.created|date:'U'}} * 1000, {{item.players}}],
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user