analytics.py 2.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. from datetime import timedelta
  2. from ariadne import QueryType
  3. from django.contrib.auth import get_user_model
  4. from django.core.cache import cache
  5. from django.utils import timezone
  6. from ...threads.models import Attachment, Post, Thread
  7. from ...users.models import DataDownload, DeletedUser
  8. CACHE_KEY = "misago_admin_analytics"
  9. CACHE_LENGTH = 3600 * 4 # 4 hours
  10. User = get_user_model()
  11. analytics = QueryType()
  12. @analytics.field("analytics")
  13. def resolve_analytics(_, info, *, span):
  14. span = clean_span(span)
  15. cache_key = "%s_%s" % (CACHE_KEY, span)
  16. data = cache.get(cache_key)
  17. if not data:
  18. data = get_data_from_db(span)
  19. cache.set(cache_key, data, CACHE_LENGTH)
  20. return data
  21. def clean_span(span):
  22. if span > 360:
  23. return 360
  24. if span < 30:
  25. return 30
  26. return span
  27. def get_data_from_db(span):
  28. analytics = Analytics(span)
  29. return {
  30. "users": analytics.get_data_for_model(User, "joined_on"),
  31. "userDeletions": analytics.get_data_for_model(DeletedUser, "deleted_on"),
  32. "threads": analytics.get_data_for_model(Thread, "started_on"),
  33. "posts": analytics.get_data_for_model(Post, "posted_on"),
  34. "attachments": analytics.get_data_for_model(Attachment, "uploaded_on"),
  35. "dataDownloads": analytics.get_data_for_model(DataDownload, "requested_on"),
  36. }
  37. class Analytics:
  38. def __init__(self, span):
  39. self.today = timezone.now()
  40. self.span = span
  41. self.cutoff = self.today - timedelta(days=span * 2)
  42. self.legend = self.get_legend()
  43. def get_legend(self):
  44. legend = []
  45. for day in range(self.span * 2):
  46. date = self.today - timedelta(days=day)
  47. legend.append(date.strftime("%x"))
  48. return legend
  49. def get_empty_data(self):
  50. return {k: 0 for k in self.legend}
  51. def get_data_for_model(self, model, date_attr):
  52. filter_kwarg = {"%s__gte" % date_attr: self.cutoff}
  53. queryset = model.objects.filter(**filter_kwarg).order_by("-pk")
  54. data = self.get_empty_data()
  55. for item in queryset.values(date_attr).iterator():
  56. date = item[date_attr].strftime("%x")
  57. if date in data:
  58. data[date] += 1
  59. values = list(data.values())
  60. current = list(reversed(values[: self.span]))
  61. previous = list(reversed(values[self.span :]))
  62. return {
  63. "current": current,
  64. "currentCumulative": cumulate_data(current),
  65. "previous": previous,
  66. "previousCumulative": cumulate_data(previous),
  67. }
  68. def cumulate_data(data_series):
  69. data = []
  70. for v in data_series:
  71. if not data:
  72. data.append(v)
  73. else:
  74. data.append(data[-1] + v)
  75. return data