123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170 |
- import math
- from datetime import datetime, timedelta
- from django.core.urlresolvers import reverse
- from django.db import models
- from django.shortcuts import redirect
- from django.template import RequestContext
- from django.utils import timezone
- from django.utils.translation import ugettext as _
- from misago.forms import FormLayout
- from misago.messages import Message
- from misago.apps.admin.stats.forms import GenerateStatisticsForm
- from misago.apps.errors import error404
- def form(request):
- """
- Allow admins to generate fancy statistic graphs for different models
- """
- statistics_providers = []
- models_map = {}
- for model in models.get_models():
- try:
- getattr(model.objects, 'filter_stats')
- statistics_providers.append((str(model.__name__).lower(), model.statistics_name))
- models_map[str(model.__name__).lower()] = model
- except AttributeError:
- pass
- if not statistics_providers:
- """
- Something went FUBAR - Misago ships with some stats providers out of box
- If those providers cant be found, this means Misago filesystem is corrupted
- """
- return request.theme.render_to_response('stats/not_available.html',
- context_instance=RequestContext(request));
- message = None
- if request.method == 'POST':
- form = GenerateStatisticsForm(request.POST, provider_choices=statistics_providers, request=request)
- if form.is_valid():
- date_start = form.cleaned_data['date_start']
- date_end = form.cleaned_data['date_end']
- if date_start > date_end:
- # Reverse dates if start is after end
- date_temp = date_end
- date_end = date_start
- date_start = date_temp
- # Assert that dates are correct
- if date_end == date_start:
- message = Message(_('Start and end date are same'), type='error')
- elif check_dates(date_start, date_end, form.cleaned_data['stats_precision']):
- message = check_dates(date_start, date_end, form.cleaned_data['stats_precision'])
- else:
- request.messages.set_flash(Message(_('Statistical report has been created.')), 'success', 'admin_stats')
- return redirect(reverse('admin_stats_graph', kwargs={
- 'model': form.cleaned_data['provider_model'],
- 'date_start': date_start.strftime('%Y-%m-%d'),
- 'date_end': date_end.strftime('%Y-%m-%d'),
- 'precision': form.cleaned_data['stats_precision']
- }))
- else:
- message = Message(form.non_field_errors()[0], 'error')
- else:
- form = GenerateStatisticsForm(provider_choices=statistics_providers, request=request)
- return request.theme.render_to_response('stats/form.html', {
- 'form': FormLayout(form),
- 'message': message,
- }, context_instance=RequestContext(request));
- def graph(request, model, date_start, date_end, precision):
- """
- Generate fancy graph for model and stuff
- """
- if date_start == date_end:
- # Bad dates
- raise error404()
- # Turn stuff into datetime's
- date_start = datetime.strptime(date_start, '%Y-%m-%d')
- date_end = datetime.strptime(date_end, '%Y-%m-%d')
- statistics_providers = []
- models_map = {}
- for model_obj in models.get_models():
- try:
- getattr(model_obj.objects, 'filter_stats')
- statistics_providers.append((str(model_obj.__name__).lower(), model_obj.statistics_name))
- models_map[str(model_obj.__name__).lower()] = model_obj
- except AttributeError:
- pass
- if not statistics_providers:
- # Like before, q.q on lack of models
- return request.theme.render_to_response('stats/not_available.html',
- context_instance=RequestContext(request));
- if not model in models_map or check_dates(date_start, date_end, precision):
- # Bad model name or graph data!
- raise error404()
- form = GenerateStatisticsForm(
- provider_choices=statistics_providers,
- request=request,
- initial={'provider_model': model, 'date_start': date_start, 'date_end': date_end, 'stats_precision': precision})
- return request.theme.render_to_response('stats/graph.html', {
- 'title': models_map[model].statistics_name,
- 'graph': build_graph(models_map[model], date_start, date_end, precision),
- 'form': FormLayout(form),
- 'message': request.messages.get_message('admin_stats'),
- }, context_instance=RequestContext(request));
- def check_dates(date_start, date_end, precision):
- date_diff = date_end - date_start
- date_diff = date_diff.seconds + date_diff.days * 86400
- if ((precision == 'day' and date_diff / 86400 > 60)
- or (precision == 'week' and date_diff / 604800 > 60)
- or (precision == 'month' and date_diff / 2592000 > 60)
- or (precision == 'year' and date_diff / 31536000 > 60)):
- return Message(_('Too many many items to display on graph.'), 'error')
- elif ((precision == 'day' and date_diff / 86400 < 1)
- or (precision == 'week' and date_diff / 604800 < 1)
- or (precision == 'month' and date_diff / 2592000 < 1)
- or (precision == 'year' and date_diff / 31536000 < 1)):
- return Message(_('Too few items to display on graph'), 'error')
- return None
- def build_graph(model, date_start, date_end, precision):
- if precision == 'day':
- format = 'F j, Y'
- step = 86400
- if precision == 'week':
- format = 'W, Y'
- step = 604800
- if precision == 'month':
- format = 'F, Y'
- step = 2592000
- if precision == 'year':
- format = 'Y'
- step = 31536000
- date_end = timezone.make_aware(date_end, timezone.get_current_timezone())
- date_start = timezone.make_aware(date_start, timezone.get_current_timezone())
- date_diff = date_end - date_start
- date_diff = date_diff.seconds + date_diff.days * 86400
- steps = int(math.ceil(float(date_diff / step))) + 1
- timeline = [0 for i in range(0, steps)]
- for i in range(0, steps):
- step_date = date_end - timedelta(seconds=(i * step));
- timeline[steps - i - 1] = step_date
- stat = {'total': 0, 'max': 0, 'stat': [0 for i in range(0, steps)], 'timeline': timeline, 'start': date_start, 'end': date_end, 'format': format}
- # Loop model items
- for item in model.objects.filter_stats(date_start, date_end).iterator():
- date_diff = date_end - item.get_date()
- date_diff = date_diff.seconds + date_diff.days * 86400
- date_diff = steps - int(math.floor(float(date_diff / step))) - 2
- stat['stat'][date_diff] += 1
- stat['total'] += 1
- # Find max
- for i in stat['stat']:
- if i > stat['max']:
- stat['max'] = i
- return stat
|