diff --git a/ponyconf/static/css/ponyconf.css b/ponyconf/static/css/ponyconf.css index e69de29..af37051 100644 --- a/ponyconf/static/css/ponyconf.css +++ b/ponyconf/static/css/ponyconf.css @@ -0,0 +1,3 @@ +tr.clickable-row { + cursor: pointer; +} diff --git a/proposals/forms.py b/proposals/forms.py index 0c2ad92..4017471 100644 --- a/proposals/forms.py +++ b/proposals/forms.py @@ -1,4 +1,4 @@ -from django.forms import CheckboxSelectMultiple, ModelForm +from django import forms from django.forms.models import modelform_factory from django.core.exceptions import ValidationError from django.utils.translation import ugettext_lazy as _ @@ -8,7 +8,19 @@ from django_select2.forms import Select2TagWidget from proposals.models import Talk, Topic, Event, Conference -class TalkForm(ModelForm): +STATUS_CHOICES = [ + ('pending', 'Pending decision'), + ('accepted', 'Accepted'), + ('declined', 'Declined'), +] +STATUS_VALUES = [ + ('pending', None), + ('accepted', True), + ('declined', False), +] + + +class TalkForm(forms.ModelForm): def __init__(self, *args, **kwargs): site = kwargs.pop('site') super(TalkForm, self).__init__(*args, **kwargs) @@ -18,14 +30,39 @@ class TalkForm(ModelForm): class Meta: model = Talk fields = ['title', 'abstract', 'description', 'topics', 'notes', 'event', 'speakers'] - widgets = {'topics': CheckboxSelectMultiple(), 'speakers': Select2TagWidget()} + widgets = {'topics': forms.CheckboxSelectMultiple(), 'speakers': Select2TagWidget()} help_texts = { 'abstract': _('Should be less than 255 characters'), 'notes': _('If you want to add some precisions for the organizers.'), } -class TopicCreateForm(ModelForm): +class FilterForm(forms.Form): + kind = forms.MultipleChoiceField( + required=False, + widget=forms.CheckboxSelectMultiple, + choices=[], + ) + status = forms.MultipleChoiceField( + required=False, + widget=forms.CheckboxSelectMultiple, + choices=STATUS_CHOICES, + ) + topic = forms.MultipleChoiceField( + required=False, + widget=forms.CheckboxSelectMultiple, + choices=[], + ) + def __init__(self, *args, **kwargs): + site = kwargs.pop('site') + super().__init__(*args, **kwargs) + events = Event.objects.filter(site=site) + self.fields['kind'].choices = events.values_list('pk', 'name') + topics = Topic.objects.filter(site=site) + self.fields['topic'].choices = topics.values_list('slug', 'name') + + +class TopicCreateForm(forms.ModelForm): def __init__(self, *args, **kwargs): self.site_id = kwargs.pop('site_id') super(TopicCreateForm, self).__init__(*args, **kwargs) diff --git a/proposals/models.py b/proposals/models.py index 9460103..7af4ab8 100644 --- a/proposals/models.py +++ b/proposals/models.py @@ -42,7 +42,7 @@ class Topic(PonyConfModel): return self.name def get_absolute_url(self): - return reverse('list-talks-by-topic', kwargs={'topic': self.slug}) + return reverse('list-talks') + '?filter=topic:%s' % self.slug class Event(models.Model): @@ -52,6 +52,7 @@ class Event(models.Model): class Meta: unique_together = ('site', 'name') + ordering = ('pk',) def __str__(self): return ugettext(self.name) diff --git a/proposals/templates/proposals/_talk_list.html b/proposals/templates/proposals/_talk_list.html index 8520ca7..1f2d51c 100644 --- a/proposals/templates/proposals/_talk_list.html +++ b/proposals/templates/proposals/_talk_list.html @@ -13,7 +13,7 @@ {% if talk.topics.exists %} {% trans "in" %} {% for topic in talk.topics.all %} - {{ topic }} + {{ topic }} {% if forloop.revcounter == 2 %} {% trans "and" %} {% elif not forloop.last %}, {% endif %} {% endfor %} {% endif %} diff --git a/proposals/templates/proposals/talk_detail.html b/proposals/templates/proposals/talk_detail.html index 3601aa8..75643ae 100644 --- a/proposals/templates/proposals/talk_detail.html +++ b/proposals/templates/proposals/talk_detail.html @@ -38,7 +38,7 @@ {% for topic in talk.topics.all %} {% if forloop.first %}{% endif %} {% empty %} {% trans "No topics." %} diff --git a/proposals/templates/proposals/talk_list.html b/proposals/templates/proposals/talk_list.html index e00cd0c..9b3d21e 100644 --- a/proposals/templates/proposals/talk_list.html +++ b/proposals/templates/proposals/talk_list.html @@ -1,11 +1,101 @@ {% extends 'base.html' %} +{% load bootstrap3 i18n %} + {% block talktab %} class="active"{% endblock %} {% block content %} -

{{ title }}

+

{% trans "Talks" %}

-{% include "proposals/_talk_list.html" %} +Show filtering options… + +

+ +
+
+
+
+
+ {% bootstrap_field filter_form.kind layout="horizontal" %} +
+
+ {% bootstrap_field filter_form.status layout="horizontal" %} +
+
+ {% bootstrap_field filter_form.topic layout="horizontal" %} +
+
+ +
+
+
+ + + + + + + + + + + + {% for talk in talk_list %} + {% if forloop.first %} + + {% endif %} + + + + + + + + {% if forloop.last%} + + {% endif %} + {% endfor %} + + + + + +
{% trans "Title" %} {% trans "Intervention kind" %} {% trans "Speakers" %} {% trans "Topics" %} {% trans "Status" %}
{{ talk.title }}{{ talk.event }} + {% for speaker in talk.speakers.all %} + {{ speaker }} + {% if forloop.revcounter == 2 %} {% trans "and" %} {% elif not forloop.last %}, {% endif %} + {% empty %}– + {% endfor %} + + {% for topic in talk.topics.all %} + {{ topic }} + {% if forloop.revcounter == 2 %} {% trans "and" %} {% elif not forloop.last %}, {% endif %} + {% empty %}– + {% endfor %} + + {% if talk.accepted == True %} + {% trans "Accepted" %} + {% elif talk.accepted == False %} + {% trans "Declined" %} + {% else %} + {% blocktrans with score=talk.score %}Pending, score: {{ score }}{% endblocktrans %} + {% endif %} +
{% trans "Total:" %} {{ talk_list|length }} {% trans "talk" %}{{ talk_list|length|pluralize }}
{% endblock %} + +{% block js_end %} + +{% endblock %} diff --git a/proposals/templates/proposals/talks.html b/proposals/templates/proposals/talks.html deleted file mode 100644 index dcc1355..0000000 --- a/proposals/templates/proposals/talks.html +++ /dev/null @@ -1,23 +0,0 @@ -{% extends 'base.html' %} - -{% load i18n %} - -{% block talktab %} class="active"{% endblock %} - -{% block content %} - -

{% trans "Talks" %}

- -{% trans "Propose a talk" %} - -{% if my_talks %} -

{% trans "My participing talks" %}

- {% include "proposals/_talk_list.html" with talk_list=my_talks %} -{% endif %} - -{% if other_talks %} -

{% trans "Others talks" %}

- {% include "proposals/_talk_list.html" with talk_list=other_talks %} -{% endif %} - -{% endblock %} diff --git a/proposals/urls.py b/proposals/urls.py index 85c2bfa..13f082d 100644 --- a/proposals/urls.py +++ b/proposals/urls.py @@ -12,7 +12,7 @@ urlpatterns = [ url(r'^talk/edit/(?P[-\w]+)$', views.talk_edit, name='edit-talk'), url(r'^talk/vote/(?P[-\w]+)/(?P[-0-2]+)$', views.vote, name='vote'), url(r'^talk/details/(?P[-\w]+)$', views.TalkDetail.as_view(), name='show-talk'), - url(r'^talk/by-topic/(?P[-\w]+)$', views.talk_list_by_topic, name='list-talks-by-topic'), + #url(r'^talk/by-topic/(?P[-\w]+)$', views.talk_list_by_topic, name='list-talks-by-topic'), url(r'^talk/accept/(?P[-\w]+)/$', views.talk_decide, {'accepted': True}, name='accept-talk'), url(r'^talk/decline/(?P[-\w]+)/$', views.talk_decide, {'accepted': False}, name='decline-talk'), url(r'^topic/$', views.TopicList.as_view(), name='list-topics'), diff --git a/proposals/views.py b/proposals/views.py index 29be02e..5fc41da 100644 --- a/proposals/views.py +++ b/proposals/views.py @@ -1,3 +1,5 @@ +from functools import reduce + from django.contrib import messages from django.contrib.auth.decorators import login_required from django.contrib.auth.mixins import LoginRequiredMixin @@ -18,7 +20,7 @@ from accounts.decorators import orga_required, staff_required from conversations.models import ConversationWithParticipant, ConversationAboutTalk, Message -from .forms import TalkForm, TopicCreateForm, TopicUpdateForm, ConferenceForm +from .forms import TalkForm, TopicCreateForm, TopicUpdateForm, ConferenceForm, FilterForm, STATUS_VALUES from .models import Talk, Topic, Vote, Conference from .signals import talk_added, talk_edited from .utils import allowed_talks, markdown_to_html @@ -50,7 +52,7 @@ def conference(request): @login_required def participate(request): - talks = Talk.objects.filter(site=get_current_site(request))#.filter(Q(speakers=request.user) | Q(proposer=request.user)).distinct() + talks = Talk.objects.filter(site=get_current_site(request)) my_talks = talks.filter(speakers=request.user) proposed_talks = talks.exclude(speakers=request.user).filter(proposer=request.user) return render(request, 'proposals/participate.html', { @@ -60,20 +62,24 @@ def participate(request): @staff_required def talk_list(request): - talks = allowed_talks(Talk.objects.filter(site=get_current_site(request)), request) + show_filters = False + talks = Talk.objects.filter(site=get_current_site(request)) + filter_form = FilterForm(request.GET or None, site=get_current_site(request)) + if filter_form.is_valid(): + data = filter_form.cleaned_data + if len(data['kind']): + talks = talks.filter(reduce(lambda x, y: x | y, [Q(event__pk=pk) for pk in data['kind']])) + if len(data['status']): + talks = talks.filter(reduce(lambda x, y: x | y, [Q(accepted=dict(STATUS_VALUES)[status]) for status in data['status']])) + if len(data['topic']): + talks = talks.filter(reduce(lambda x, y: x | y, [Q(topics__slug=topic) for topic in data['topic']])) + show_filters = True return render(request, 'proposals/talk_list.html', { - 'title': _('Talks') + ' (%d)' % len(talks), + 'show_filters': show_filters, 'talk_list': talks, + 'filter_form': filter_form, }) - -@login_required -def talk_list_by_topic(request, topic): - topic = get_object_or_404(Topic, slug=topic) - talks = allowed_talks(Talk.objects.filter(site=topic.site, topics=topic), request) - return render(request, 'proposals/talk_list.html', {'title': _('Talks related to %s:') % topic, 'talk_list': talks}) - - @login_required def talk_edit(request, talk=None): if talk: