speaker list filtering

This commit is contained in:
Élie Bouttier 2016-09-18 18:43:32 +02:00
parent 7fe3582730
commit 802e1a36a3
7 changed files with 159 additions and 27 deletions

View File

@ -2,6 +2,7 @@ from django.contrib import admin
from accounts.models import Participation, Profile, Transport, Connector
admin.site.register(Profile) # FIXME extend user admin
admin.site.register(Participation)
admin.site.register(Transport)

View File

@ -8,7 +8,7 @@ class TalkAdmin(admin.ModelAdmin):
# (it is easy to obtain incoherent data due to site framework)
def has_add_permission(self, request):
return False
# Filter for 'on site' tocpis and event
# Filter for 'on site' topics and event
def get_form(self, request, obj=None, **kwargs):
form = super(TalkAdmin, self).get_form(request, obj, **kwargs)
# in fact, obj should never be none as 'add' button is disabled

View File

@ -7,6 +7,8 @@ from django_select2.forms import Select2TagWidget
from proposals.models import Talk, Topic, Event, Conference
from accounts.models import Transport
STATUS_CHOICES = [
('pending', 'Pending decision'),
@ -37,7 +39,7 @@ class TalkForm(forms.ModelForm):
}
class FilterForm(forms.Form):
class TalkFilterForm(forms.Form):
kind = forms.MultipleChoiceField(
required=False,
widget=forms.CheckboxSelectMultiple,
@ -62,6 +64,25 @@ class FilterForm(forms.Form):
self.fields['topic'].choices = topics.values_list('slug', 'name')
class SpeakerFilterForm(forms.Form):
transport = forms.MultipleChoiceField(
required=False,
widget=forms.CheckboxSelectMultiple,
choices=Transport.objects.values_list('pk', 'name'),
)
hosting = forms.MultipleChoiceField(
required=False,
widget=forms.CheckboxSelectMultiple,
choices=[
('hotel', 'Hotel'),
('homestay', 'Homestay'),
],
)
sound = forms.NullBooleanField()
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
class TopicCreateForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.site_id = kwargs.pop('site_id')

View File

@ -1,22 +1,111 @@
{% extends 'base.html' %}
{% load i18n %}
{% load bootstrap3 i18n %}
{% block speakertab %} class="active"{% endblock %}
{% block content %}
<h1>{% trans "Speakers" %} ({{ user_list|length }})</h1>
<h1>{% trans "Speakers" %}</h1>
<ul>
{% for speaker in user_list %}
<li>
<a href="{% url 'show-speaker' username=speaker.username %}">{{ speaker.profile }}</a>
({{ speaker.talk_set.count }} {% trans "talk" %}{{ speaker.talk_set.count|pluralize }})
<a class="btn btn-primary" role="button" data-toggle="collapse" href="#filter" aria-expanded="{{ show_filters|yesno:"true,false" }}" aria-controles="filter">{% trans "Show filtering options…" %}</a>
<br /><br />
<div class="collapse{{ show_filters|yesno:" in," }}" id="filter">
<div class="well">
<form class="form-horizontal" method="get">
<div class="row">
<div class="col-md-4 col-xs-6">
{% bootstrap_field filter_form.transport layout="horizontal" %}
</div>
<div class="col-md-4 col-xs-6">
{% bootstrap_field filter_form.hosting layout="horizontal" %}
</div>
<div class="col-md-4 col-xs-6">
{% bootstrap_field filter_form.sound layout="horizontal" %}
</div>
</div>
<input type="submit" class="btn btn-success" value="{% trans "Filter" %}">
</form>
</div>
</div>
<table class="table table-bordered table-hover">
<thead>
<tr>
<th class="text-center">{% trans "Username" %}</th>
<th class="text-center">{% trans "Fullname" %}</th>
<th class="text-center">{% trans "Talk count" %}</th>
<th class="text-center">{% blocktrans context "table column title" %}Need transport?{% endblocktrans %}</th>
<th class="text-center">{% blocktrans context "table column title" %}Need hosting?{% endblocktrans %}</th>
<th class="text-center">{% trans "Need sound?" %}</th>
<th class="text-center"></th>
</tr>
</thead>
{% for speaker in speaker_list %}
{% if forloop.first %}
<tbody>
{% endif %}
<tr class="clickable-row" data-href="{% url 'show-speaker' username=speaker.user.username %}">
<td>{{ speaker.user.username }}</td>
<td>{{ speaker.user.get_full_name }}</td>
<td class="text-right">{{ speaker.talk_set.count }}</td>
{% if speaker.need_transport %}
<td class="warning">
{% for transport in speaker.transport.all %}
{% if not forloop.first %}, {% endif %}
{{ transport }}
{% endfor %}
</td>
{% else %}
<td></td>
{% endif %}
{% if speaker.need_hosting %}
<td class="warning">
{% if speaker.homestay %}
Logement chez habitant
{% else %}
Hotel
{% endif %}
</td>
{% else %}
<td></td>
{% endif %}
{% if speaker.sound %}
<td class="warning">Yes</td>
{% else %}
<td>No</td>
{% endif %}
<td>
<a class="btn btn-primary" href="{% url 'conversation' speaker.user.username %}">{% trans "Contact" %}</a>
</td>
</tr>
</li>
{% empty %}
<li><i>{% trans "No speakers." %}</i></li>
{% endfor %}
</ul>
{% if forloop.last %}
</tbody>
{% endif %}
{% endfor %}
<tfoot>
<tr>
<th colspan="5">{% trans "Total:" %} {{ speaker_list|length }} {% trans "speaker" %}{{ speaker_list|length|pluralize }}</th>
</tr>
</tfoot>
</table>
{% endblock %}
{% block js_end %}
<script type="text/javascript">
jQuery(document).ready(function($) {
$(".clickable-row").click(function() {
window.location = $(this).data("href");
});
var anchor = window.location.hash.replace("#", "");
if (anchor == "filter") {
$("#filter").collapse('show');
}
});
</script>
{% endblock %}

View File

@ -8,7 +8,7 @@
<h1>{% trans "Talks" %}</h1>
<a class="btn btn-primary" role="button" data-toggle="collapse" href="#filter" aria-expanded="{{ show_filters|yesno:"true,false" }}" aria-controls="filter">Show filtering options…</a>
<a class="btn btn-primary" role="button" data-toggle="collapse" href="#filter" aria-expanded="{{ show_filters|yesno:"true,false" }}" aria-controls="filter">{% trans "Show filtering options…" %}</a>
<br /><br />
@ -26,7 +26,7 @@
{% bootstrap_field filter_form.topic layout="horizontal" %}
</div>
</div>
<input type="submit" class="btn btn-success" value="Filter">
<input type="submit" class="btn btn-success" value="{% trans "Filter" %}">
</form>
</div>
</div>

View File

@ -12,12 +12,11 @@ urlpatterns = [
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/by-topic/(?P<topic>[-\w]+)$', views.talk_list_by_topic, name='list-talks-by-topic'),
url(r'^talk/accept/(?P<talk>[-\w]+)/$', views.talk_decide, {'accepted': True}, name='accept-talk'),
url(r'^talk/decline/(?P<talk>[-\w]+)/$', views.talk_decide, {'accepted': False}, name='decline-talk'),
url(r'^topic/$', views.TopicList.as_view(), name='list-topics'),
url(r'^topic/add/$', views.TopicCreate.as_view(), name='add-topic'),
url(r'^topic/(?P<slug>[-\w]+)/edit/$', views.TopicUpdate.as_view(), name='edit-topic'),
url(r'^speakers/$', views.SpeakerList.as_view(), name='list-speakers'),
url(r'^speakers/$', views.speaker_list, name='list-speakers'),
url(r'^speaker/(?P<username>[\w.@+-]+)$', views.user_details, name='show-speaker'),
]

View File

@ -20,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, FilterForm, STATUS_VALUES
from .forms import TalkForm, TopicCreateForm, TopicUpdateForm, ConferenceForm, TalkFilterForm, STATUS_VALUES, SpeakerFilterForm
from .models import Talk, Topic, Vote, Conference
from .signals import talk_added, talk_edited
from .utils import allowed_talks, markdown_to_html
@ -64,7 +64,7 @@ def participate(request):
def talk_list(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))
filter_form = TalkFilterForm(request.GET or None, site=get_current_site(request))
# Filtering
if filter_form.is_valid():
data = filter_form.cleaned_data
@ -192,14 +192,6 @@ class TopicUpdate(OrgaRequiredMixin, TopicMixin, TopicFormMixin, UpdateView):
return TopicCreateForm if self.request.user.is_superuser else TopicUpdateForm
class SpeakerList(StaffRequiredMixin, ListView):
template_name = 'proposals/speaker_list.html'
def get_queryset(self):
site = get_current_site(self.request)
return User.objects.filter(talk__in=Talk.objects.filter(site=site)).all().distinct()
@login_required
def vote(request, talk, score):
talk = get_object_or_404(Talk, site=get_current_site(request), slug=talk)
@ -242,6 +234,36 @@ def talk_decide(request, talk, accepted):
})
@staff_required
def speaker_list(request):
show_filters = False
site = get_current_site(request)
speakers = Participation.objects.filter(user__talk__in=Talk.objects.filter(site=site)).all().distinct()
filter_form = SpeakerFilterForm(request.GET or None)
# Filtering
if filter_form.is_valid():
data = filter_form.cleaned_data
if len(data['transport']):
show_filters = True
speakers = speakers.filter(reduce(lambda x, y: x | y, [Q(transport__pk=pk) for pk in data['transport']]))
if len(data['hosting']):
show_filters = True
queries = []
if 'hotel' in data['hosting']:
queries += [ Q(need_hosting=True, homestay=False) ]
if 'homestay' in data['hosting']:
queries += [ Q(need_hosting=True, homestay=True) ]
speakers = speakers.filter(reduce(lambda x, y: x | y, queries))
if data['sound'] != None:
show_filters = True
speakers = speakers.filter(sound=data['sound'])
return render(request, 'proposals/speaker_list.html', {
'speaker_list': speakers,
'filter_form': filter_form,
'show_filters': show_filters,
})
@login_required
def user_details(request, username):
user = get_object_or_404(User, username=username)