vote views, urls, tests, templates, etc.

This commit is contained in:
Guilhem Saurel 2016-07-02 01:03:07 +02:00
parent 58256e0d3b
commit 8e936ccf46
6 changed files with 70 additions and 14 deletions

View File

@ -13,6 +13,8 @@ from sortedm2m.fields import SortedManyToManyField
from accounts.models import Participation from accounts.models import Participation
from ponyconf.utils import PonyConfModel, enum_to_choices from ponyconf.utils import PonyConfModel, enum_to_choices
from .utils import query_sum
__all__ = ['Topic', 'Talk'] __all__ = ['Topic', 'Talk']
@ -65,6 +67,9 @@ class Talk(PonyConfModel):
def is_editable_by(self, user): def is_editable_by(self, user):
return user == self.proposer or user in self.speakers.all() or self.is_moderable_by(user) return user == self.proposer or user in self.speakers.all() or self.is_moderable_by(user)
def score(self):
return query_sum(self.vote_set, 'vote')
class Vote(PonyConfModel): class Vote(PonyConfModel):
@ -74,3 +79,9 @@ class Vote(PonyConfModel):
class Meta: class Meta:
unique_together = ('talk', 'user') unique_together = ('talk', 'user')
def __str__(self):
return "%+i by %s for %s" % (self.vote, self.user, self.talk)
def get_absolute_url(self):
return self.talk.get_absolute_url()

View File

@ -12,11 +12,11 @@
<p>{{ talk.get_event_display }}</p> <p>{{ talk.get_event_display }}</p>
<b>Description:</b> <h3>Description:</h3>
<p>{{ talk.description }}</p> <p>{{ talk.description }}</p>
<b>Speakers:</b> <h3>Speakers:</h3>
<ul> <ul>
{% for speaker in talk.speakers.all %} {% for speaker in talk.speakers.all %}
@ -26,7 +26,7 @@
{% endfor %} {% endfor %}
</ul> </ul>
<b>Topics:</b> <h3>Topics:</h3>
<ul> <ul>
{% for topic in talk.topics.all %} {% for topic in talk.topics.all %}
@ -37,7 +37,18 @@
{% if moderate_perm %} {% if moderate_perm %}
<h2>Moderation</h2> <h2>Moderation</h2>
<b>Messages:</b> <h3>Vote:</h3>
<div class="btn-group" role="group" aria-label="vote">
<a class="btn {% if vote.vote == -2 %} active {% endif %}btn-danger" href="{% url 'vote' talk=talk.slug score='-2' %}">-2</a>
<a class="btn {% if vote.vote == -1 %} active {% endif %}btn-warning" href="{% url 'vote' talk=talk.slug score='-1' %}">-1</a>
<a class="btn {% if vote.vote == 0 %} active {% endif %}btn-default" href="{% url 'vote' talk=talk.slug score='0' %}"> 0</a>
<a class="btn {% if vote.vote == 1 %} active {% endif %}btn-info" href="{% url 'vote' talk=talk.slug score='1' %}">+1</a>
<a class="btn {% if vote.vote == 2 %} active {% endif %}btn-success" href="{% url 'vote' talk=talk.slug score='2' %}">+2</a>
</div>
Sum: {{ talk.score }}
<h3>Messages:</h3>
{% for message in talk.conversation.messages.all %} {% for message in talk.conversation.messages.all %}
{% include 'conversations/_message_detail.html' %} {% include 'conversations/_message_detail.html' %}
{% endfor %} {% endfor %}

View File

@ -5,7 +5,7 @@ from django.test import TestCase
from accounts.models import Participation from accounts.models import Participation
from .models import Talk, Topic from .models import Talk, Topic, Vote
class ProposalsTests(TestCase): class ProposalsTests(TestCase):
@ -43,17 +43,26 @@ class ProposalsTests(TestCase):
{'title': 'mega talk', 'description': 'mega', 'event': 1}).status_code, 403) {'title': 'mega talk', 'description': 'mega', 'event': 1}).status_code, 403)
self.assertEqual(self.client.get(reverse('list-talks')).status_code, 200) self.assertEqual(self.client.get(reverse('list-talks')).status_code, 200)
# Vote
self.assertEqual(talk.score(), 0)
self.assertEqual(self.client.get(reverse('vote', kwargs={'talk': talk.slug, 'score': 2})).status_code, 403)
self.client.login(username='c', password='c')
self.assertEqual(self.client.get(reverse('vote', kwargs={'talk': talk.slug, 'score': 2})).status_code, 302)
self.assertEqual(talk.score(), 2)
# Models str & get_asbolute_url # Models str & get_asbolute_url
for model in [Talk, Topic]: for model in [Talk, Topic, Vote]:
item = model.objects.first() item = model.objects.first()
self.assertEqual(self.client.get(item.get_absolute_url()).status_code, 200) self.assertEqual(self.client.get(item.get_absolute_url()).status_code, 200)
self.assertTrue(str(item)) self.assertTrue(str(item))
# Talk.is_editable_by # Talk.is_{editable,moderable}_by
a, b, c = User.objects.all() a, b, c = User.objects.all()
self.assertTrue(talk.is_editable_by(c)) self.assertTrue(talk.is_moderable_by(c))
self.assertFalse(talk.is_editable_by(b)) self.assertFalse(talk.is_editable_by(b))
self.assertFalse(talk.is_moderable_by(b))
self.client.login(username='a', password='a') self.client.login(username='a', password='a')
self.client.post(reverse('edit-talk', kwargs={'talk': 'super-talk'}), self.client.post(reverse('edit-talk', kwargs={'talk': 'super-talk'}),
{'title': 'mega talk', 'description': 'mega', 'event': 1, 'speakers': "2,1"}) {'title': 'mega talk', 'description': 'mega', 'event': 1, 'speakers': "2,1"})
self.assertTrue(talk.is_editable_by(b)) self.assertTrue(talk.is_editable_by(b))
self.assertFalse(talk.is_moderable_by(b))

View File

@ -7,6 +7,7 @@ urlpatterns = [
url(r'^talk/$', views.talk_list, name='list-talks'), url(r'^talk/$', views.talk_list, name='list-talks'),
url(r'^talk/add/$', views.talk_edit, name='add-talk'), url(r'^talk/add/$', views.talk_edit, name='add-talk'),
url(r'^talk/edit/(?P<talk>[-\w]+)$', views.talk_edit, name='edit-talk'), url(r'^talk/edit/(?P<talk>[-\w]+)$', views.talk_edit, name='edit-talk'),
url(r'^talk/vote/(?P<talk>[-\w]+)/(?P<score>[-0-2]+)$', views.vote, name='vote'),
url(r'^talk/details/(?P<slug>[-\w]+)$', views.TalkDetail.as_view(), name='show-talk'), url(r'^talk/details/(?P<slug>[-\w]+)$', views.TalkDetail.as_view(), name='show-talk'),
url(r'^talk/by-topic/(?P<topic>[-\w]+)$', views.talk_list_by_topic, name='list-talks-by-topic'), url(r'^talk/by-topic/(?P<topic>[-\w]+)$', views.talk_list_by_topic, name='list-talks-by-topic'),
url(r'^talk/by-speaker/(?P<speaker>[\w.@+-]+)$', views.talk_list_by_speaker, name='list-talks-by-speaker'), url(r'^talk/by-speaker/(?P<speaker>[\w.@+-]+)$', views.talk_list_by_speaker, name='list-talks-by-speaker'),

6
proposals/utils.py Normal file
View File

@ -0,0 +1,6 @@
from django.db.models import Sum
from django.db.models.functions import Coalesce
def query_sum(queryset, field):
return queryset.aggregate(s=Coalesce(Sum(field), 0))['s']

View File

@ -12,7 +12,7 @@ from accounts.mixins import StaffRequiredMixin
from accounts.models import Participation from accounts.models import Participation
from .forms import TalkForm from .forms import TalkForm
from .models import Talk, Topic from .models import Talk, Topic, Vote
from .signals import new_talk from .signals import new_talk
@ -81,11 +81,15 @@ def talk_edit(request, talk=None):
class TalkDetail(LoginRequiredMixin, DetailView): class TalkDetail(LoginRequiredMixin, DetailView):
queryset = Talk.on_site.all() queryset = Talk.on_site.all()
def get_context_data(self, **kwargs): def get_context_data(self, **ctx):
kwargs['edit_perm'] = self.object.is_editable_by(self.request.user) user = self.request.user
kwargs['moderate_perm'] = self.object.is_moderable_by(self.request.user) if self.object.is_moderable_by(user):
kwargs['form_url'] = reverse('talk-conversation', kwargs={'talk': self.object.slug}) vote = Vote.objects.filter(talk=self.object, user=Participation.on_site.get(user=user)).first()
return super().get_context_data(**kwargs) ctx.update(edit_perm=True, moderate_perm=True, vote=vote,
form_url=reverse('talk-conversation', kwargs={'talk': self.object.slug}))
else:
ctx['edit_perm'] = self.object.is_editable_by(user)
return super().get_context_data(**ctx)
class TopicList(LoginRequiredMixin, ListView): class TopicList(LoginRequiredMixin, ListView):
@ -102,6 +106,20 @@ class SpeakerList(StaffRequiredMixin, ListView):
template_name = 'proposals/speaker_list.html' template_name = 'proposals/speaker_list.html'
@login_required
def vote(request, talk, score):
site = get_current_site(request)
talk = get_object_or_404(Talk, site=site, slug=talk)
user = Participation.on_site.get(user=request.user)
if not talk.is_moderable_by(request.user):
raise PermissionDenied()
vote, created = Vote.objects.get_or_create(talk=talk, user=user)
vote.vote = int(score)
vote.save()
messages.success(request, "Vote successfully %s" % ('created' if created else 'updated'))
return redirect(talk.get_absolute_url())
@login_required @login_required
def user_details(request, username): def user_details(request, username):
return render(request, 'proposals/user_details.html', { return render(request, 'proposals/user_details.html', {