frequently.views: 136 total statements, 100.0% covered

Generated: Thu 2015-09-10 13:30 CEST

Source file: /home/tyrdall/projects/django-frequently/src/frequently/views.py

Stats: 120 executed, 0 missed, 16 excluded, 99 ignored

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