Commit 1125cbf6 authored by Nicolas Joyard's avatar Nicolas Joyard

Add autocomplete to search forms

parent c334858e
......@@ -11,7 +11,7 @@ setup(name='political-memory',
author_email='cortex@worlddomination.be',
url='http://github.com/political-memory/political_memory/',
install_requires=[
'django-autocomplete-light==3.1.6',
'django-autocomplete-light==3.2.0',
'django-autoslug>=1.9,<1.10',
'django-bootstrap3>=6,<7',
'django-coffeescript>=0.7,<0.8',
......
from .forms import DossierSearchForm, RepresentativeSearchForm
from memopol_settings.models import Setting
from representatives.models import Chamber, Group
def search_form_options(request):
d = {}
# Note: Those queries needs to be eval in the template so that we can cache
# it efficiently
d['chambers'] = Chamber.objects.all()
d['countries'] = Group.objects.filter(kind='country')
d['parties'] = Group.objects.filter(kind='group')
d['delegations'] = Group.objects.filter(kind='delegation')
d['committees'] = Group.objects.filter(kind='committee')
return d
def search_forms(request):
return {
'representative_search_form': RepresentativeSearchForm(request.GET),
'dossier_search_form': DossierSearchForm(request.GET)
}
def intro_text(request):
......
from django import forms
from dal import autocomplete, forward
from representatives.models import Chamber, Group, Representative
from representatives_votes.models import Dossier
class RepresentativeSearchForm(forms.Form):
search = forms.CharField(
required=False,
label='Name',
widget=forms.TextInput(attrs={'placeholder': ''})
)
scoremin = forms.FloatField(
required=False,
label='Between',
widget=forms.NumberInput(attrs={'placeholder': 'Min. score'})
)
scoremax = forms.FloatField(
required=False,
label='and',
widget=forms.NumberInput(attrs={'placeholder': 'Max. score'})
)
chamber = forms.ModelChoiceField(
queryset=Chamber.objects.all(),
required=False,
widget=autocomplete.ModelSelect2(
url='chamber-autocomplete',
)
)
country = forms.ModelChoiceField(
queryset=Group.objects.filter(kind='country'),
required=False,
widget=autocomplete.ModelSelect2(
url='group-autocomplete',
forward=(forward.Const('country', 'kind'),)
)
)
party = forms.ModelChoiceField(
queryset=Group.objects.filter(kind='group'),
required=False,
widget=autocomplete.ModelSelect2(
url='group-autocomplete',
forward=(forward.Const('group', 'kind'),)
)
)
committee = forms.ModelChoiceField(
queryset=Group.objects.filter(kind='committee'),
required=False,
widget=autocomplete.ModelSelect2(
url='group-autocomplete',
forward=(forward.Const('committee', 'kind'),)
)
)
delegation = forms.ModelChoiceField(
queryset=Group.objects.filter(kind='delegation'),
required=False,
widget=autocomplete.ModelSelect2(
url='group-autocomplete',
forward=(forward.Const('delegation', 'kind'),)
)
)
class DossierSearchForm(forms.Form):
search = forms.CharField(
required=False,
label='Name',
widget=forms.TextInput(attrs={'placeholder': ''})
)
chamber = forms.ModelChoiceField(
queryset=Chamber.objects.all(),
required=False,
widget=autocomplete.ModelSelect2(
url='chamber-autocomplete',
)
)
......@@ -133,7 +133,7 @@ TEMPLATE_LOADERS = (
TEMPLATE_CONTEXT_PROCESSORS = global_settings.TEMPLATE_CONTEXT_PROCESSORS + (
'django.template.context_processors.request',
'memopol.context_processors.search_form_options',
'memopol.context_processors.search_forms',
'memopol.context_processors.intro_text'
)
......
......@@ -99,7 +99,7 @@ label {
font-weight: 400;
}
.form-control, input, textarea {
.form-control, .control-label, input, textarea {
color: #212121 !important;
}
......@@ -129,7 +129,7 @@ label {
box-shadow: inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(102,175,233,0.6);
}
textarea, textarea.form-control, input.form-control, input[type=text], input[type=password], input[type=email], input[type=number], [type=text].form-control, [type=password].form-control, [type=email].form-control, [type=tel].form-control, [contenteditable].form-control {
textarea, textarea.form-control, input.form-control, input[type=text], input[type=number], input[type=password], input[type=email], input[type=number], [type=text].form-control, [type=number].form-control, [type=password].form-control, [type=email].form-control, [type=tel].form-control, [contenteditable].form-control {
padding: 0;
border: none;
border-radius: 0;
......@@ -138,7 +138,7 @@ textarea, textarea.form-control, input.form-control, input[type=text], input[typ
box-shadow: inset 0 -1px 0 #ddd;
}
textarea:focus, textarea.form-control:focus, input.form-control:focus, input[type=text]:focus, input[type=password]:focus, input[type=email]:focus, input[type=number]:focus, [type=text].form-control:focus, [type=password].form-control:focus, [type=email].form-control:focus, [type=tel].form-control:focus, [contenteditable].form-control:focus {
textarea:focus, textarea.form-control:focus, input.form-control:focus, input[type=text]:focus, input[type=number]:focus, input[type=password]:focus, input[type=email]:focus, input[type=number]:focus, [type=text].form-control:focus, [type=number].form-control:focus, [type=password].form-control:focus, [type=email].form-control:focus, [type=tel].form-control:focus, [contenteditable].form-control:focus {
-webkit-box-shadow: inset 0 -2px 0 #487ED6;
box-shadow: inset 0 -2px 0 #487ED6;
}
......
{% load i18n memopol_tags %}
{% load fontawesome %}
{% load bootstrap3 %}
<form class="form-horizontal hidden-print" method="GET" action="{% url "representative-list" %}">
<div class="input-group">
......@@ -30,97 +31,20 @@
aria-expanded="{% if view == 'representative_list' and filter.data %}true{% else %}false{% endif %}" id="form-rep">
<form id="rep-search-form" class="form-horizontal" method="GET" action="{% url "representative-list" %}">
<div class="form-group">
<label class="col-sm-3" for="search-rep">{% trans "Name" %}</label>
<div class="col-sm-9">
<input type="text" class="form-control" name="search" id="search-rep" value="{{ filter.data.search }}">
{% bootstrap_field representative_search_form.search layout='horizontal' %}
<div class="row">
<div class="col-md-7">
{% bootstrap_field representative_search_form.scoremin layout='horizontal' horizontal_label_class='col-md-5' horizontal_field_class='col-md-7' placeholder='prout' %}
</div>
</div>
<div class="form-group">
<label class="col-sm-3" for="score-min">{% trans "Score between" %}</label>
<div class="col-sm-4">
<input type="number" class="form-control" name="scoremin" id="score-min" value="{{ filter.data.scoremin }}">
</div>
<label class="col-sm-1" for="score-max">{% trans "and" %}</label>
<div class="col-sm-4">
<input type="number" class="form-control" name="scoremax" id="score-max" value="{{ filter.data.scoremax }}">
</div>
</div>
<div class="form-group">
<label class="col-sm-3" for="chamber-rep">{% trans "Chamber" %}</label>
<div class="col-sm-9">
<select class="form-control" id="chamber-rep" name="chamber">
<option value="">{% trans "All" %}</option>
{% for chamber in chambers %}
<option {% if filter.data.chamber = chamber.id|cast_str %}selected{% endif %} value="{{ chamber.id }}">
{{ chamber.name }}
</option>
{% endfor %}
</select>
</div>
</div>
<div class="form-group">
<label class="col-sm-3" for="country">{% trans "Country" %}</label>
<div class="col-sm-9">
<select class="form-control" id="country" name="country">
<option value="">{% trans "All" %}</option>
{% for country in countries %}
<option {% if filter.data.country = country.id|cast_str %}selected{% endif %} value="{{ country.id }}">
{% if country.abbreviation %}{{ country.abbreviation }} &ndash;{% endif %}
{{ country.name }}
</option>
{% endfor %}
</select>
</div>
</div>
<div class="form-group">
<label class="col-sm-3" for="party">{% trans "Party" %}</label>
<div class="col-sm-9">
<select class="form-control" id="party" name="party">
<option value="">{% trans "All" %}</option>
{% for party in parties %}
<option {% if filter.data.party = party.pk|cast_str %}selected{% endif %} value="{{ party.pk }}">
{% if party.abbreviation %}{{ party.abbreviation }} &ndash;{% endif %}
{{ party.name }}
</option>
{% endfor %}
</select>
<div class="col-md-5">
{% bootstrap_field representative_search_form.scoremax layout='horizontal' horizontal_label_class='col-md-2' horizontal_field_class='col-md-10' %}
</div>
</div>
<div class="form-group">
<label class="col-sm-3" for="committee">{% trans "Committee" %}</label>
<div class="col-sm-9">
<select class="form-control" id="committee" name="committee">
<option value="">{% trans "All" %}</option>
{% for committee in committees %}
<option {% if filter.data.committee = committee.pk|cast_str %}selected{% endif %} value="{{ committee.pk }}" data-url="{% url "representative-list" %}?committee={{ committee.pk }}">
{% if committee.abbreviation %}{{ committee.abbreviation }} &ndash;{% endif %}
{{ committee.name }}
</option>
{% endfor %}
</select>
</div>
</div>
<div class="form-group">
<label class="col-sm-3" for="delegation">{% trans "Delegation" %}</label>
<div class="col-sm-9">
<select class="form-control" id="delegation" name="delegation">
<option value="">{% trans "All" %}</option>
{% for delegation in delegations %}
<option {% if filter.data.delegation = delegation.pk|cast_str %}selected{% endif %} value="{{ delegation.pk }}" data-url="{% url "representative-list" %}?delegation={{ delegation.pk }}">
{{ delegation.name }}
</option>
{% endfor %}
</select>
</div>
</div>
{% bootstrap_field representative_search_form.chamber layout='horizontal' %}
{% bootstrap_field representative_search_form.country layout='horizontal' %}
{% bootstrap_field representative_search_form.party layout='horizontal' %}
{% bootstrap_field representative_search_form.committee layout='horizontal' %}
{% bootstrap_field representative_search_form.delegation layout='horizontal' %}
<button type="submit" class="btn btn-default">{% trans "Search" %}</button>
</form>
</div>
......@@ -133,26 +57,8 @@
<div class="collapse{% if view == 'dossier_list' and filter.data %} in{% endif %}"
aria-expanded="{% if view == 'dossier_list' and filter.data %}true{% else %}false{% endif %}" id="form-dossier">
<form id="dossier-search-form" class="form-horizontal" method="GET" action="{% url "dossier-list" %}">
<div class="form-group">
<label class="col-sm-3" for="search-dossier">{% trans "Name" %}</label>
<div class="col-sm-9">
<input type="text" class="form-control" name="search" id="search-dossier" value="{{ filter.data.search }}">
</div>
</div>
<div class="form-group">
<label class="col-sm-3" for="chamber-dossier">{% trans "Chamber" %}</label>
<div class="col-sm-9">
<select class="form-control" name="chamber" id="chamber-dossier">
<option value="">{% trans "All" %}</option>
{% for chamber in chambers %}
<option {% if filter.data.chamber = chamber.id|cast_str %}selected{% endif %} value="{{ chamber.id }}">
{{ chamber.name }}
</option>
{% endfor %}
</select>
</div>
</div>
{% bootstrap_field dossier_search_form.search layout='horizontal' %}
{% bootstrap_field dossier_search_form.chamber layout='horizontal' %}
<button type="submit" class="btn btn-default">{% trans "Search" %}</button>
</form>
......
......@@ -9,6 +9,8 @@ from views.autocomplete import (
ProposalAutocomplete,
RepresentativeAutocomplete,
ThemeAutocomplete,
ChamberAutocomplete,
GroupAutocomplete,
)
from views.charts import ThemeScoresJSONView, ChamberScoresJSONView
......@@ -146,6 +148,17 @@ urlpatterns = [
name='theme-autocomplete',
),
url(
r'^autocomplete/chamber/$',
ChamberAutocomplete.as_view(),
name='chamber-autocomplete',
),
url(
r'^autocomplete/group/$',
GroupAutocomplete.as_view(),
name='group-autocomplete',
),
# Theme list
......
......@@ -4,7 +4,7 @@ from dal import autocomplete
from django.db.models import Q
from representatives.models import Representative
from representatives.models import Representative, Chamber, Group
from representatives_votes.models import Proposal
from memopol_themes.models import Theme
......@@ -43,3 +43,28 @@ class ThemeAutocomplete(autocomplete.Select2QuerySetView):
qs = qs.filter(name__icontains=self.q)
return qs
class ChamberAutocomplete(autocomplete.Select2QuerySetView):
def get_queryset(self):
qs = Chamber.objects.all()
if self.q:
qs = qs.filter(Q(name__icontains=self.q) |
Q(abbreviation__icontains=self.q))
return qs
class GroupAutocomplete(autocomplete.Select2QuerySetView):
def get_queryset(self):
qs = Group.objects.all()
kind = self.forwarded.get('kind', None)
if kind:
qs = qs.filter(kind=kind)
if self.q:
qs = qs.filter(Q(name__icontains=self.q))
return qs
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment