views.py 10.0 KB

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