analytics.py 2.6 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  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
  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. "threads": analytics.get_data_for_model(Thread, "started_on"),
  32. "posts": analytics.get_data_for_model(Post, "posted_on"),
  33. "attachments": analytics.get_data_for_model(Attachment, "uploaded_on"),
  34. "dataDownloads": analytics.get_data_for_model(DataDownload, "requested_on"),
  35. }
  36. class Analytics:
  37. def __init__(self, span):
  38. self.today = timezone.now()
  39. self.span = span
  40. self.cutoff = self.today - timedelta(days=span * 2)
  41. self.legend = self.get_legend()
  42. def get_legend(self):
  43. legend = []
  44. for day in range(self.span * 2):
  45. date = self.today - timedelta(days=day)
  46. legend.append(date.strftime("%x"))
  47. return legend
  48. def get_empty_data(self):
  49. return {k: 0 for k in self.legend}
  50. def get_data_for_model(self, model, date_attr):
  51. filter_kwarg = {"%s__gte" % date_attr: self.cutoff}
  52. queryset = model.objects.filter(**filter_kwarg).order_by("-pk")
  53. data = self.get_empty_data()
  54. for item in queryset.values(date_attr).iterator():
  55. date = item[date_attr].strftime("%x")
  56. if date in data:
  57. data[date] += 1
  58. values = list(data.values())
  59. current = list(reversed(values[: self.span]))
  60. previous = list(reversed(values[self.span :]))
  61. return {
  62. "current": current,
  63. "currentCumulative": cumulate_data(current),
  64. "previous": previous,
  65. "previousCumulative": cumulate_data(previous),
  66. }
  67. def cumulate_data(data_series):
  68. data = []
  69. for v in data_series:
  70. if not data:
  71. data.append(v)
  72. else:
  73. data.append(data[-1] + v)
  74. return data