frequently.views: 131 total statements, 100.0% covered

Generated: Fri 2013-09-20 15:24 CEST

Source file: /Users/martin/Repos/django-frequently/frequently/views.py

Stats: 121 executed, 0 missed, 10 excluded, 96 ignored

  1. """
  2. Views for the ``django-frequently`` application.
  3. """
  4. from math import fsum
  5. from django.contrib.auth.models import User
  6. from django.core.urlresolvers import reverse
  7. from django.http import HttpResponse, HttpResponseNotFound
  8. from django.template.response import TemplateResponse
  9. from django.utils import timezone
  10. from django.views.generic import CreateView, DetailView, ListView
  11. from django_libs.views_mixins import AccessMixin
  12. from .forms import EntryForm
  13. from .models import Entry, EntryCategory, Feedback
  14. class EntryMixin(object):
  15. """
  16. Mixin to handle and arrange the entry list.
  17. """
  18. def get_ordered_entries(self, queryset=False):
  19. """
  20. Custom ordering. First we get the average views and rating for
  21. the categories's entries. Second we created a rank by multiplying
  22. both. Last, we sort categories by this rank from top to bottom.
  23. Example:
  24. - Cat_1
  25. - Entry_1 (500 Views, Rating 2)
  26. - Entry_2 (200 Views, Rating -4)
  27. - Entry_3 (100 Views, Rating 3)
  28. - Cat_2
  29. - Entry_1 (200 Views, Rating 7)
  30. - Entry_2 (50 Views, Rating 2)
  31. Result:
  32. Cat_1 has a rank by: 88.88 (avg. views: 266.66, avg. rating: 0.33)
  33. Cat_2 has a rank by: 562.5 (avg. views: 125, avg. rating: 4.5)
  34. Cat_2 will be displayed at the top. The algorithm is quality-oriented,
  35. as you can see.
  36. """
  37. if queryset:
  38. self.queryset = queryset
  39. else:
  40. self.queryset = EntryCategory.objects.all()
  41. if self.queryset:
  42. for category in self.queryset:
  43. entries = category.get_entries()
  44. if entries:
  45. amount_list = [e.amount_of_views for e in entries]
  46. rating_list = [e.rating() for e in entries]
  47. views_per_entry = fsum(amount_list) / len(amount_list)
  48. rating_per_entry = fsum(rating_list) / len(rating_list)
  49. category.last_rank = views_per_entry * rating_per_entry
  50. category.save()
  51. else:
  52. self.queryset = self.queryset.exclude(pk=category.pk)
  53. self.queryset = sorted(self.queryset, key=lambda c: c.last_rank,
  54. reverse=True)
  55. return self.queryset
  56. def post(self, request, *args, **kwargs):
  57. if "get_answer" in request.POST.keys():
  58. entry = Entry.objects.get(pk=request.POST['get_answer'])
  59. entry.last_view_date = timezone.now()
  60. entry.amount_of_views += 1
  61. entry.save()
  62. return TemplateResponse(
  63. request,
  64. 'frequently/partials/answer.html',
  65. {
  66. 'entry': entry,
  67. 'rated_entries': self.request.session.get(
  68. 'rated_entries', False),
  69. },
  70. )
  71. self.feedback = Feedback()
  72. if "user_id" in request.POST.keys():
  73. try:
  74. user_id = int(request.POST.get('user_id'))
  75. try:
  76. self.feedback.user = User.objects.get(pk=user_id)
  77. except User.DoesNotExist:
  78. pass
  79. except ValueError:
  80. pass
  81. if 'ratingID' in request.POST.keys() and request.is_ajax():
  82. try:
  83. entry_id = int(request.POST.get('ratingID').replace(
  84. 'ratingID', ''))
  85. try:
  86. entry = Entry.objects.get(pk=entry_id)
  87. return HttpResponse(entry.rating())
  88. except Entry.DoesNotExist:
  89. return HttpResponseNotFound()
  90. except ValueError:
  91. return HttpResponseNotFound()
  92. for key in request.POST.keys():
  93. if key.startswith('up') or key.startswith('down'):
  94. try:
  95. entry_id = int(key.replace('up', '').replace('down', ''))
  96. try:
  97. entry = Entry.objects.get(pk=entry_id)
  98. except Entry.DoesNotExist:
  99. return HttpResponseNotFound()
  100. except ValueError:
  101. return HttpResponseNotFound()
  102. if not request.session.get('rated_entries', False):
  103. request.session['rated_entries'] = []
  104. if not entry.pk in request.session['rated_entries']:
  105. request.session['rated_entries'].append(entry.pk)
  106. request.session.modified = True
  107. self.feedback.entry = entry
  108. if key.startswith('up'):
  109. entry.upvotes += 1
  110. self.feedback.validation = "P"
  111. if key.startswith('down'):
  112. entry.downvotes += 1
  113. self.feedback.validation = "N"
  114. entry.save()
  115. self.feedback.save()
  116. if request.is_ajax():
  117. return TemplateResponse(
  118. request,
  119. 'frequently/partials/feedback_form.html',
  120. {
  121. 'feedback_entry': entry.pk,
  122. 'feedback': self.feedback,
  123. },
  124. )
  125. elif key.startswith('feedback'):
  126. try:
  127. feedback_id = int(key.replace('feedback', ''))
  128. try:
  129. self.feedback = Feedback.objects.get(pk=feedback_id)
  130. except Feedback.DoesNotExist:
  131. return HttpResponseNotFound()
  132. except ValueError:
  133. return HttpResponseNotFound()
  134. self.feedback.remark = request.POST.get("remark")
  135. self.feedback.save()
  136. if request.is_ajax():
  137. return TemplateResponse(
  138. request,
  139. 'frequently/partials/feedback_form.html',
  140. {'feedback_send': True},
  141. )
  142. return self.get(self, request, *args, **kwargs)
  143. class EntryCategoryListView(AccessMixin, EntryMixin, ListView):
  144. """
  145. Main view to display all categories and their entries.
  146. """
  147. model = EntryCategory
  148. template_name = "frequently/entry_list.html"
  149. access_mixin_setting_name = 'FREQUENTLY_ALLOW_ANONYMOUS'
  150. def get_queryset(self):
  151. """
  152. Customized to get the ordered categories and entries from the Mixin.
  153. """
  154. self.queryset = super(EntryCategoryListView, self).get_queryset()
  155. return self.get_ordered_entries(self.queryset)
  156. class EntryDetailView(AccessMixin, EntryMixin, DetailView):
  157. """
  158. Main view to display one entry.
  159. """
  160. model = Entry
  161. template_name = "frequently/entry_list.html"
  162. access_mixin_setting_name = 'FREQUENTLY_ALLOW_ANONYMOUS'
  163. def get_object(self, **kwargs):
  164. obj = super(EntryDetailView, self).get_object(**kwargs)
  165. obj.last_view_date = timezone.now()
  166. obj.amount_of_views += 1
  167. obj.save()
  168. return obj
  169. def get_context_data(self, **kwargs):
  170. context = super(EntryDetailView, self).get_context_data(**kwargs)
  171. context.update({
  172. 'rated_entries': self.request.session.get('rated_entries', False),
  173. 'object_list': self.get_ordered_entries(),
  174. })
  175. for key in self.request.POST.keys():
  176. if key.startswith('down') or key.startswith('up'):
  177. context.update({
  178. 'feedback_entry': int(
  179. key.replace('up', '').replace('down', '')),
  180. 'feedback': self.feedback,
  181. })
  182. return context
  183. return context
  184. class EntryCreateView(AccessMixin, CreateView):
  185. """
  186. Feedback submission form view.
  187. """
  188. model = Entry
  189. form_class = EntryForm
  190. access_mixin_setting_name = 'FREQUENTLY_ALLOW_ANONYMOUS'
  191. def get_form_kwargs(self):
  192. kwargs = super(EntryCreateView, self).get_form_kwargs()
  193. if self.request.user.is_authenticated():
  194. kwargs.update({
  195. 'owner': self.request.user,
  196. })
  197. return kwargs
  198. def get_success_url(self):
  199. return reverse('frequently_list')