Development of an internal social media platform with personalised dashboards for students
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

views.py 6.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. # -*- coding: utf-8 -*-
  2. import warnings
  3. from collections import namedtuple
  4. from django.http import Http404, JsonResponse, HttpResponseBadRequest
  5. from django.conf import settings
  6. from django.views.generic import View, DetailView
  7. from hitcount.utils import get_ip
  8. from hitcount.models import Hit, HitCount
  9. from hitcount.utils import RemovedInHitCount13Warning
  10. class HitCountMixin(object):
  11. """
  12. Mixin to evaluate a HttpRequest and a HitCount and determine whether or not
  13. the HitCount should be incremented and the Hit recorded.
  14. """
  15. @classmethod
  16. def hit_count(self, request, hitcount):
  17. """
  18. Called with a HttpRequest and HitCount object it will return a
  19. namedtuple:
  20. UpdateHitCountResponse(hit_counted=Boolean, hit_message='Message').
  21. `hit_counted` will be True if the hit was counted and False if it was
  22. not. `'hit_message` will indicate by what means the Hit was either
  23. counted or ignored.
  24. """
  25. UpdateHitCountResponse = namedtuple(
  26. 'UpdateHitCountResponse', 'hit_counted hit_message')
  27. # as of Django 1.8.4 empty sessions are not being saved
  28. # https://code.djangoproject.com/ticket/25489
  29. if request.session.session_key is None:
  30. request.session.save()
  31. user = request.user
  32. try:
  33. is_authenticated_user = user.is_authenticated()
  34. except:
  35. is_authenticated_user = user.is_authenticated
  36. session_key = request.session.session_key
  37. ip = get_ip(request)
  38. user_agent = request.META.get('HTTP_USER_AGENT', '')[:255]
  39. hits_per_ip_limit = getattr(settings, 'HITCOUNT_HITS_PER_IP_LIMIT', 0)
  40. exclude_user_group = getattr(settings, 'HITCOUNT_EXCLUDE_USER_GROUP', None)
  41. # third, see if we are excluding a specific user group or not
  42. if exclude_user_group and is_authenticated_user:
  43. if user.groups.filter(name__in=exclude_user_group):
  44. return UpdateHitCountResponse(
  45. False, 'Not counted: user excluded by group')
  46. # eliminated first three possible exclusions, now on to checking our database of
  47. # active hits to see if we should count another one
  48. # start with a fresh active query set (HITCOUNT_KEEP_HIT_ACTIVE)
  49. qs = Hit.objects.filter_active()
  50. # check limit on hits from a unique ip address (HITCOUNT_HITS_PER_IP_LIMIT)
  51. if hits_per_ip_limit:
  52. if qs.filter(ip__exact=ip).count() >= hits_per_ip_limit:
  53. return UpdateHitCountResponse(
  54. False, 'Not counted: hits per IP address limit reached')
  55. # create a generic Hit object with request data
  56. hit = Hit(session=session_key, hitcount=hitcount, ip=get_ip(request),
  57. user_agent=request.META.get('HTTP_USER_AGENT', '')[:255],)
  58. # first, use a user's authentication to see if they made an earlier hit
  59. if is_authenticated_user:
  60. if not qs.filter(user=user, hitcount=hitcount):
  61. hit.user = user # associate this hit with a user
  62. hit.save()
  63. response = UpdateHitCountResponse(
  64. True, 'Hit counted: user authentication')
  65. else:
  66. response = UpdateHitCountResponse(
  67. False, 'Not counted: authenticated user has active hit')
  68. # if not authenticated, see if we have a repeat session
  69. else:
  70. if not qs.filter(session=session_key, hitcount=hitcount):
  71. hit.save()
  72. response = UpdateHitCountResponse(
  73. True, 'Hit counted: session key')
  74. else:
  75. response = UpdateHitCountResponse(
  76. False, 'Not counted: session key has active hit')
  77. return response
  78. class HitCountJSONView(View, HitCountMixin):
  79. """
  80. JSON response view to handle HitCount POST.
  81. """
  82. def dispatch(self, request, *args, **kwargs):
  83. if not request.is_ajax():
  84. raise Http404()
  85. return super(HitCountJSONView, self).dispatch(request, *args, **kwargs)
  86. def get(self, request, *args, **kwargs):
  87. msg = "Hits counted via POST only."
  88. return JsonResponse({'success': False, 'error_message': msg})
  89. def post(self, request, *args, **kwargs):
  90. hitcount_pk = request.POST.get('hitcountPK')
  91. try:
  92. hitcount = HitCount.objects.get(pk=hitcount_pk)
  93. except:
  94. return HttpResponseBadRequest("HitCount object_pk not working")
  95. hit_count_response = self.hit_count(request, hitcount)
  96. return JsonResponse(hit_count_response._asdict())
  97. class HitCountDetailView(DetailView, HitCountMixin):
  98. """
  99. HitCountDetailView provides an inherited DetailView that will inject the
  100. template context with a `hitcount` variable giving you the number of
  101. Hits for an object without using a template tag.
  102. Optionally, by setting `count_hit = True` you can also do the business of
  103. counting the Hit for this object (in lieu of using JavaScript). It will
  104. then further inject the response from the attempt to count the Hit into
  105. the template context.
  106. """
  107. count_hit = False
  108. def get_context_data(self, **kwargs):
  109. context = super(HitCountDetailView, self).get_context_data(**kwargs)
  110. if self.object:
  111. hit_count = HitCount.objects.get_for_object(self.object)
  112. hits = hit_count.hits
  113. context['hitcount'] = {'pk': hit_count.pk}
  114. if self.count_hit:
  115. hit_count_response = self.hit_count(self.request, hit_count)
  116. if hit_count_response.hit_counted:
  117. hits = hits + 1
  118. context['hitcount']['hit_counted'] = hit_count_response.hit_counted
  119. context['hitcount']['hit_message'] = hit_count_response.hit_message
  120. context['hitcount']['total_hits'] = hits
  121. return context
  122. def _update_hit_count(request, hitcount):
  123. """
  124. Deprecated in 1.2. Use hitcount.views.Hit CountMixin.hit_count() instead.
  125. """
  126. warnings.warn(
  127. "hitcount.views._update_hit_count is deprecated. "
  128. "Use hitcount.views.HitCountMixin.hit_count() instead.",
  129. RemovedInHitCount13Warning
  130. )
  131. return HitCountMixin.hit_count(request, hitcount)
  132. def update_hit_count_ajax(request, *args, **kwargs):
  133. """
  134. Deprecated in 1.2. Use hitcount.views.HitCountJSONView instead.
  135. """
  136. warnings.warn(
  137. "hitcount.views.update_hit_count_ajax is deprecated. "
  138. "Use hitcount.views.HitCountJSONView instead.",
  139. RemovedInHitCount13Warning
  140. )
  141. view = HitCountJSONView.as_view()
  142. return view(request, *args, **kwargs)