views.py 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. from datetime import datetime, timedelta
  2. from django.conf import settings
  3. from django.core.urlresolvers import reverse
  4. from django.db import models
  5. from django.http import Http404
  6. from django.shortcuts import redirect
  7. from django.template import RequestContext
  8. from django.utils import formats, timezone
  9. from django.utils.translation import ugettext as _
  10. import math
  11. from misago.forms import FormLayout
  12. from misago.forums.models import Thread, Post
  13. from misago.messages import Message, BasicMessage
  14. from misago.overview.admin.forms import GenerateStatisticsForm
  15. from misago.sessions.models import Session
  16. from misago.users.models import User
  17. def overview_home(request):
  18. return request.theme.render_to_response('overview/home.html', {
  19. 'users': request.monitor['users'],
  20. 'users_inactive': request.monitor['users_inactive'],
  21. 'threads': request.monitor['threads'],
  22. 'posts': request.monitor['posts'],
  23. 'admins': Session.objects.filter(user__isnull=False).filter(admin=1).order_by('user__username_slug').select_related(depth=1),
  24. }, context_instance=RequestContext(request));
  25. def overview_stats(request):
  26. """
  27. Allow admins to generate fancy statistic graphs for different models
  28. """
  29. statistics_providers = []
  30. models_map = {}
  31. for model in models.get_models():
  32. try:
  33. getattr(model.objects, 'filter_overview')
  34. statistics_providers.append((str(model.__name__).lower(), model.statistics_name))
  35. models_map[str(model.__name__).lower()] = model
  36. except AttributeError:
  37. pass
  38. if not statistics_providers:
  39. """
  40. Something went FUBAR - Misago ships with some stats providers out of box
  41. If those providers cant be found, this means Misago filesystem is corrupted
  42. """
  43. return request.theme.render_to_response('overview/stats/not_available.html',
  44. context_instance=RequestContext(request));
  45. message = None
  46. if request.method == 'POST':
  47. form = GenerateStatisticsForm(request.POST, provider_choices=statistics_providers, request=request)
  48. if form.is_valid():
  49. date_start = form.cleaned_data['date_start']
  50. date_end = form.cleaned_data['date_end']
  51. if date_start > date_end:
  52. # Reverse dates if start is after end
  53. date_temp = date_end
  54. date_end = date_start
  55. date_start = date_temp
  56. # Assert that dates are correct
  57. if date_end == date_start:
  58. message = BasicMessage(_('Start and end date are same'), type='error')
  59. elif check_dates(date_start, date_end, form.cleaned_data['stats_precision']):
  60. message = check_dates(date_start, date_end, form.cleaned_data['stats_precision'])
  61. else:
  62. request.messages.set_flash(BasicMessage(_('Statistical report has been created.')), 'success', 'admin_stats')
  63. return redirect(reverse('admin_overview_graph', kwargs={
  64. 'model': form.cleaned_data['provider_model'],
  65. 'date_start': date_start.strftime('%Y-%m-%d'),
  66. 'date_end': date_end.strftime('%Y-%m-%d'),
  67. 'precision': form.cleaned_data['stats_precision']
  68. }))
  69. else:
  70. message = Message(request, form.non_field_errors()[0])
  71. message.type = 'error'
  72. else:
  73. form = GenerateStatisticsForm(provider_choices=statistics_providers, request=request)
  74. return request.theme.render_to_response('overview/stats/form.html', {
  75. 'form': FormLayout(form),
  76. 'message': message,
  77. }, context_instance=RequestContext(request));
  78. def overview_graph(request, model, date_start, date_end, precision):
  79. """
  80. Generate fancy graph for model and stuff
  81. """
  82. if date_start == date_end:
  83. # Bad dates
  84. raise Http404()
  85. # Turn stuff into datetime's
  86. date_start = datetime.strptime(date_start, '%Y-%m-%d')
  87. date_end = datetime.strptime(date_end, '%Y-%m-%d')
  88. statistics_providers = []
  89. models_map = {}
  90. for model_obj in models.get_models():
  91. try:
  92. getattr(model_obj.objects, 'filter_overview')
  93. statistics_providers.append((str(model_obj.__name__).lower(), model_obj.statistics_name))
  94. models_map[str(model_obj.__name__).lower()] = model_obj
  95. except AttributeError:
  96. pass
  97. if not statistics_providers:
  98. # Like before, q.q on lack of models
  99. return request.theme.render_to_response('overview/stats/not_available.html',
  100. context_instance=RequestContext(request));
  101. if not model in models_map or check_dates(date_start, date_end, precision):
  102. # Bad model name or graph data!
  103. raise Http404()
  104. form = GenerateStatisticsForm(
  105. provider_choices=statistics_providers,
  106. request=request,
  107. initial={'provider_model': model, 'date_start': date_start, 'date_end': date_end, 'stats_precision': precision})
  108. return request.theme.render_to_response('overview/stats/graph.html', {
  109. 'title': models_map[model].statistics_name,
  110. 'graph': build_graph(models_map[model], date_start, date_end, precision),
  111. 'form': FormLayout(form),
  112. 'message': request.messages.get_message('admin_stats'),
  113. }, context_instance=RequestContext(request));
  114. def check_dates(date_start, date_end, precision):
  115. date_diff = date_end - date_start
  116. date_diff = date_diff.seconds + date_diff.days * 86400
  117. if ((precision == 'day' and date_diff / 86400 > 60)
  118. or (precision == 'week' and date_diff / 604800 > 60)
  119. or (precision == 'month' and date_diff / 2592000 > 60)
  120. or (precision == 'year' and date_diff / 31536000 > 60)):
  121. return BasicMessage(_('Too many many items to display on graph.'), type='error')
  122. elif ((precision == 'day' and date_diff / 86400 < 1)
  123. or (precision == 'week' and date_diff / 604800 < 1)
  124. or (precision == 'month' and date_diff / 2592000 < 1)
  125. or (precision == 'year' and date_diff / 31536000 < 1)):
  126. return BasicMessage(_('Too few items to display on graph'), type='error')
  127. return None
  128. def overview_forums(request, mode=None):
  129. return request.theme.render_to_response('overview/forums.html', {
  130. 'graph_posts': build_stat(Post, mode),
  131. 'graph_threads': build_stat(Thread, mode),
  132. 'posts': request.monitor['posts'],
  133. 'threads': request.monitor['threads'],
  134. 'mode': mode,
  135. }, context_instance=RequestContext(request));
  136. def build_graph(model, date_start, date_end, precision):
  137. if precision == 'day':
  138. format = 'F j, Y'
  139. step = 86400
  140. if precision == 'week':
  141. format = 'W, Y'
  142. step = 604800
  143. if precision == 'month':
  144. format = 'F, Y'
  145. step = 2592000
  146. if precision == 'year':
  147. format = 'Y'
  148. step = 31536000
  149. date_end = timezone.make_aware(date_end, timezone.get_current_timezone())
  150. date_start = timezone.make_aware(date_start, timezone.get_current_timezone())
  151. date_diff = date_end - date_start
  152. date_diff = date_diff.seconds + date_diff.days * 86400
  153. steps = int(math.ceil(float(date_diff / step))) + 1
  154. timeline = [0 for i in range(0, steps)]
  155. for i in range(0, steps):
  156. step_date = date_end - timedelta(seconds=(i * step));
  157. timeline[steps - i - 1] = step_date
  158. stat = {'total': 0, 'max': 0, 'stat': [0 for i in range(0, steps)], 'timeline': timeline, 'start': date_start, 'end': date_end, 'format': format}
  159. # Loop model items
  160. for item in model.objects.filter_overview(date_start, date_end).iterator():
  161. date_diff = date_end - item.get_date()
  162. date_diff = date_diff.seconds + date_diff.days * 86400
  163. date_diff = steps - int(math.floor(float(date_diff / step))) - 2
  164. stat['stat'][date_diff] += 1
  165. stat['total'] += 1
  166. # Find max
  167. for i in stat['stat']:
  168. if i > stat['max']:
  169. stat['max'] = i
  170. return stat