Commit 24f83ba8 authored by Nicolas Joyard's avatar Nicolas Joyard
Browse files

Merge branch 'v3'

parents 107b5321 52cf504a
......@@ -22,7 +22,12 @@ memopol_env
pip-log.txt
pip-delete-this-directory.txt
.dpl
db.sqlite
*.sqlite
*.json.xz
log/
/data
data/
# editors
*~
......@@ -15,6 +15,8 @@ python:
services:
- postgresql
install:
- pip install -U setuptools
- pip install git+https://github.com/njoyard/django-responsediff.git@fix-selectors-encoding
- pip install -e .[testing]
before_script:
- bin/install_client_deps.sh
......@@ -39,12 +41,3 @@ deploy:
repo: political-memory/political_memory
branch: master
- provider: openshift
user: memopol@laquadrature.net
password:
secure: atDq1NEkHXOsV2gZKeXAIn+PvbL3jduz3WK1qIs7BSHyNbrZMT1OUmvoXXrM8+i5eqW3TNsvp23w0RuD06wxSjHkPl+ZCEXP1Ao98p85UZNCgixxiwZHEhL6Amz5vqueGhv+47VOIKNgNFb9NAtRrWyIdA9xDUiK2oWkMSDmHas=
app: v3
domain: memopol
deployment_branch: v3
on:
repo: political-memory/political_memory
branch: v3
......@@ -8,7 +8,7 @@
# starting with 'downloadFromGithub' at the bottom.
# The last argument is expected to be a git ref (ie
# a branch name, tag or commit-ish).
#
#
set -e
......@@ -37,8 +37,6 @@ set -e
mkdir -p ${DEST}
downloadFromGithub jquery jquery/jquery 2.1.4
downloadFromGithub fontawesome FortAwesome/Font-Awesome v4.3.0
downloadFromGithub flag-icon-css lipis/flag-icon-css 0.7.1
downloadFromGithub bootstrap twbs/bootstrap v3.3.5
echo "* Done."
......@@ -39,7 +39,7 @@ export DJANGO_SETTINGS_MODULE=memopol.settings
./manage.py migrate
# Import sample data
./manage.py loaddata memopol/fixtures/smaller_sample.json
./manage.py loaddata memopol/fixtures/data_sample.json
echo
echo "You're all set!"
......
.active-block
Display :
- if active_only
current legislature only /
%a{'href': '?={queries.urlencode}&active_only=0'}
all data
- else
%a{'href': '?={queries.urlencode}&active_only=1'}
current legislature only
="/ all data"
\ No newline at end of file
{% block active %}
Display :
{% if active_only %}
current legislature only /
<a href="{queries.urlencode}&active_only=0">
all data
</a>
{% else %}
<a href="{queries.urlencode}&active_only=1">
current legislature only
</a>
/ all data
{% endif %}
{% endblock %}
.pagination-block
%nav
%ul.pagination.pagination-sm
- if page_obj.has_previous
%li
%a{'href': '?={queries.urlencode}&page=1',
'aria-label': 'First'}
<i aria-hidden="true" class="fa fa-chevron-circle-left"></i>
%li
%a{'href': '?={queries.urlencode}&page=={page_obj.previous_page_number}',
'aria-label': 'Previous'}
<i aria-hidden="true" class="fa fa-chevron-left"></i>
- for p in page_range
- if p
- if p == page_obj.number
%li.active
%a{'href': ''}
{{ p }}
- else
%li
%a{'href': '?={queries.urlencode}&page=={p}'}
{{ p }}
- if page_obj.has_next
%li
%a{'href': '?={queries.urlencode}&page=={page_obj.next_page_number}',
'aria-label': 'Next'}
<i aria-hidden="true" class="fa fa-chevron-right"></i>
%li
%a{'href': '?={queries.urlencode}&page=={paginator.num_pages}',
'aria-label': 'Last'}
<i aria-hidden="true" class="fa fa-chevron-circle-right"></i>
%div.count
.count-block
Number of results : {{ paginator.count }}
- if active_only != None
- include 'core/blocks/active-filter.html'
- if sort != None
- include 'core/blocks/sorting.html'
.page-size-block
Number of displayed results :
{{ paginator.per_page }}
(
- for limit in pagination_limits
%a{'href': '?={queries.urlencode}&paginate_by={{ limit }}'}<
{{ limit }}
- if not forloop.last
\/
)
- if grid_list
- include 'core/blocks/grid-list.html'
{% block pagination %}
<nav>
<ul class="pagination pagination-sm">
{% if page_obj.has_previous %}
<li>
<a href="{{queries.urlencode}}&page=1" aria-label="First">
<i aria-hidden="true" class="fa fa-chevron-circle-left"></i>
</a>
</li>
<li>
<a href="{queries.urlencode}&page=={page_obj.previous_page_number}" aria-label="Previous">
<i aria-hidden="true" class="fa fa-chevron-left"></i>
</a>
</li>
{% endif %}
{% for p in page_range %}
{% if p %}
{% if p == page_obj.number %}
<li class="active">
<a href=''>
{{ p }}
</a>
</li>
{% else %}
<li>
<a href="{queries.urlencode}&page=={p}">
{{ p }}
</a>
</li>
{% endif %}
{% endif %}
{% endfor %}
{% if page_obj.has_next %}
<li>
<a href=""{queries.urlencode}&page=={page_obj.next_page_number}" aria-label="Next">
<i aria-hidden="true" class="fa fa-chevron-right"></i>
</a>
</li>
<li>
<a href="{queries.urlencode}&page=={paginator.num_pages}" aria-label="Last">
<i aria-hidden="true" class="fa fa-chevron-circle-right"></i>
</a>
</li>
{% endif %}
</ul>
</nav>
<div class=".count">
{% block count %}
Number of results : {{ paginator.count }}
{% if active_only != None %}
{% include 'core/blocks/active-filter.html' %}
{% endif %}
{% if sort != None %}
{% include 'core/blocks/sorting.html' %}
{% endif %}
{% endblock %}
{% block page-size %}
Number of displayed results :
{{ paginator.per_page }}
(
{% for limit in pagination_limits %}
<a href="{queries.urlencode}&paginate_by={{ limit }}">
{{ limit }}
</a>
{% if not forloop.last %}
/
{% endif %}
{% endfor %}
)
{% if grid_list %}
{% include 'core/blocks/grid-list.html' %}
{% endif %}
{% endblock %}
</div>
{% endblock %}
......@@ -53,55 +53,66 @@ class ActiveLegislatureMixin(object):
class SortMixin(object):
"""
Mixin for views that allow sorting.
The sort_fields attribute should be defined to a {field: label} dict
containing all fields usable for sorting.
The sort_default and sort_default_dir attributes should contain the default
sorting settings.
The sort_modes attribute should be defined to a dict as such:
{
'mode1': {
'order': 42,
'label': 'mode label',
'fields': ['-field1', 'field2', ...]
},
...
}
The sort_default attribute should contain the default sorting mode.
"""
sort_fields = {}
sort_default_field = None
sort_default_dir = 'asc'
sort_modes = {}
sort_default = None
sort_session_prefix = ''
def get(self, *args, **kwargs):
self.set_sorting()
return super(SortMixin, self).get(*args, **kwargs)
def set_sorting(self):
if 'sort_by' in self.request.GET:
self.request.session['sort_by'] = self.request.GET['sort_by']
elif 'sort_by' not in self.request.session:
self.request.session['sort_by'] = self.sort_default_field
def _session_get_sort(self):
k = '%s_sort' % self.sort_session_prefix
return self.request.session[k]
def _session_set_sort(self, value):
k = '%s_sort' % self.sort_session_prefix
self.request.session[k] = value
if self.request.session['sort_by'] not in self.sort_fields:
self.request.session['sort_by'] = self.sort_default_field
def _session_sort_exists(self):
k = '%s_sort' % self.sort_session_prefix
return k in self.request.session
def set_sorting(self):
if 'sort' in self.request.GET:
self._session_set_sort(self.request.GET['sort'])
elif not self._session_sort_exists():
self._session_set_sort(self.sort_default)
if 'sort_dir' in self.request.GET:
self.request.session['sort_dir'] = self.request.GET['sort_dir']
elif 'sort_dir' not in self.request.session:
self.request.session['sort_dir'] = self.sort_default_dir
if self._session_get_sort() not in self.sort_modes:
self._session_set_sort(self.sort_default)
def get_context_data(self, **kwargs):
c = super(SortMixin, self).get_context_data(**kwargs)
c['queries'] = copy(self.request.GET)
if 'sort_by' in c['queries']:
del c['queries']['sort_by']
if 'sort_dir' in c['queries']:
del c['queries']['sort_dir']
c['sort_querystring'] = copy(self.request.GET)
if 'sort' in c['sort_querystring']:
del c['sort_querystring']['sort']
c['sort'] = {
'fields': self.sort_fields,
'field': self.request.session['sort_by'],
'dir': self.request.session['sort_dir'],
'modes': [{'id': k, 'label': v['label'], 'order': v['order']}
for k, v in self.sort_modes.iteritems()],
'mode': self._session_get_sort()
}
return c
def get_queryset(self):
qs = super(SortMixin, self).get_queryset()
if self.request.session['sort_by']:
qs = qs.order_by('%s%s' % (
'-' if self.request.session['sort_dir'] == 'desc' else '',
self.request.session['sort_by']))
if self._session_get_sort() in self.sort_modes:
mode = self.sort_modes[self._session_get_sort()]
qs = qs.order_by(*mode['fields'])
return qs
......@@ -138,9 +149,9 @@ class PaginationMixin(object):
c['pagination_limits'] = self.pagination_limits
c['paginate_by'] = self.request.session['paginate_by']
c['page_range'] = self.get_page_range(c['page_obj'])
c['queries'] = copy(self.request.GET)
if 'page' in c['queries']:
del c['queries']['page']
c['pagination_querystring'] = copy(self.request.GET)
if 'page' in c['pagination_querystring']:
del c['pagination_querystring']['page']
return c
......@@ -167,6 +178,12 @@ class GridListMixin(object):
class CSVDownloadMixin(object):
def get_context_data(self, **kwargs):
c = super(CSVDownloadMixin, self).get_context_data(**kwargs)
c['csv'] = True
c['csv_querystring'] = copy(self.request.GET)
return c
def get_paginate_by(self, queryset):
if self.request.GET.get('csv', None) is None:
return super(CSVDownloadMixin, self).get_paginate_by(queryset)
......
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
# coding: utf-8
from dal.autocomplete import ModelSelect2
import datetime
from django.db.models import Q
from django.utils.text import slugify
from django_filters import FilterSet, MethodFilter, ModelChoiceFilter
from django_filters import FilterSet, MethodFilter
from representatives.models import Chamber, Group, Representative
from representatives.models import Representative
from representatives_votes.models import Dossier
from memopol_themes.models import Theme
def rep_chamber_filter(qs, value):
today = datetime.date.today()
return qs.filter(
Q(mandates__end_date__gte=today) | Q(mandates__end_date__isnull=True),
mandates__group__chamber=value
)
class RepresentativeFilter(FilterSet):
def dossier_chamber_filter(qs, value):
return qs.filter(documents__chamber=value)
search = MethodFilter(action='search_filter')
scoremin = MethodFilter(action='score_min_filter')
scoremax = MethodFilter(action='score_max_filter')
chamber = MethodFilter(action='chamber_filter')
country = MethodFilter(action='group_filter')
party = MethodFilter(action='group_filter')
delegation = MethodFilter(action='group_filter')
committee = MethodFilter(action='group_filter')
class Meta:
model = Representative
fields = ['search', 'chamber', 'country', 'party', 'delegation',
'committee']
def group_filter(qs, value):
today = datetime.date.today()
return qs.filter(
Q(mandates__end_date__gte=today) | Q(mandates__end_date__isnull=True),
mandates__group=value
)
def search_filter(self, qs, value):
if len(value) == 0:
return qs
return qs.filter(slug__icontains=slugify(value))
class RepresentativeFilter(FilterSet):
def chamber_filter(self, qs, value):
if len(value) == 0:
return qs
search = MethodFilter(action='search_filter')
today = datetime.date.today()
return qs.filter(
Q(mandates__end_date__gte=today) |
Q(mandates__end_date__isnull=True),
mandates__group__chamber=value
)
chamber = ModelChoiceFilter(queryset=Chamber.objects.all(),
action=rep_chamber_filter)
def group_filter(self, qs, value):
if len(value) == 0:
return qs
country = ModelChoiceFilter(queryset=Group.objects.filter(kind='country'),
action=group_filter)
today = datetime.date.today()
return qs.filter(
Q(mandates__end_date__gte=today) |
Q(mandates__end_date__isnull=True),
mandates__group=value
)
group = ModelChoiceFilter(queryset=Group.objects.exclude(
kind__in=['chamber', 'country']),
action=group_filter,
widget=ModelSelect2(url='group-autocomplete'),
label='Party, committee or delegation')
def score_min_filter(self, qs, value):
if len(value) == 0:
return qs
class Meta:
model = Representative
fields = ['search', 'chamber', 'country']
try:
return qs.filter(score__score__gte=int(value))
except ValueError:
return qs
def search_filter(self, qs, value):
def score_max_filter(self, qs, value):
if len(value) == 0:
return qs
return qs.filter(slug__icontains=slugify(value))
try:
return qs.filter(score__score__lte=int(value))
except ValueError:
return qs
class DossierFilter(FilterSet):
search = MethodFilter(action='search_filter')
chamber = ModelChoiceFilter(queryset=Chamber.objects.all(),
action=dossier_chamber_filter)
chamber = MethodFilter(action='chamber_filter')
class Meta:
model = Dossier
......@@ -77,7 +89,14 @@ class DossierFilter(FilterSet):
return qs
return qs.filter(Q(title__icontains=value) |
Q(reference__icontains=value))
Q(reference__icontains=value) |
Q(documents__link__icontains=value))
def chamber_filter(self, qs, value):
if len(value) == 0:
return qs
return qs.filter(documents__chamber=value)
class ThemeFilter(FilterSet):
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -85,6 +85,7 @@ INSTALLED_APPS = (
'bootstrap3',
'datetimewidget',
'django_filters',
'fontawesome',
'rest_framework',
'taggit',
# ---
......@@ -106,6 +107,20 @@ if DEBUG:
else:
INSTALLED_APPS += ('debug_toolbar',)
try:
import django_extensions # noqa
except:
pass
else:
INSTALLED_APPS += ('django_extensions',)
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.dummy.DummyCache',
}
}
MIDDLEWARE_CLASSES = (
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
......@@ -186,8 +201,6 @@ TEMPLATE_DIRS = [os.path.join(BASE_DIR, 'templates')]
TEMPLATE_LOADERS = (
'django.template.loaders.filesystem.Loader',
'django.template.loaders.app_directories.Loader',
'hamlpy.template.loaders.HamlPyFilesystemLoader',
'hamlpy.template.loaders.HamlPyAppDirectoriesLoader',
)
"""
......@@ -201,6 +214,7 @@ TEMPLATE_LOADERS = (
TEMPLATE_CONTEXT_PROCESSORS = global_settings.TEMPLATE_CONTEXT_PROCESSORS + (
'django.template.context_processors.request',
'memopol.context_processors.search_form_options',
)
# Static files finders
......@@ -225,7 +239,7 @@ if os.environ.get('OPENSHIFT_LOG_DIR', None):
COMPRESS_PRECOMPILERS = (
# ('text/coffeescript', 'coffee --compile --stdio'),
# ('text/less', 'lesscpy {infile}'),
('text/x-scss', 'django_libsass.SassCompiler'),
# ('text/x-scss', 'django_libsass.SassCompiler'),
# ('text/x-sass', 'sass {infile} {outfile}'),
# ('text/x-scss', 'sass --scss {infile} {outfile}'),
# ('text/stylus', 'stylus < {infile} > {outfile}'),
......@@ -233,9 +247,6 @@ COMPRESS_PRECOMPILERS = (
)
LIBSASS_SOURCE_COMMENTS = False
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
......
......@@ -88,25 +88,22 @@ def country_flag(country):
# Enable using groups instead of countries
code = country.code if hasattr(country, 'code') else country.abbreviation
return mark_safe(
'<span class="flag-icon flag-icon-{code}"></span> {name}'.format(
name=country.name,
'<span class="flag-icon flag-icon-{code}"></span>'.format(
code=code.lower()))
@register.filter
def chamber_icon(chamber):
def chamber_icon(chamber, tplace='bottom'):
url = static('images/chamber-%s.png' % cssify(chamber.abbreviation))
return mark_safe(
u'<span class="chamber-icon" style="background-image: url({url})">'
u'</span> {name}'.format(name=chamber.name, url=url))
@register.filter
def chamber_small_icon(chamber):
url = static('images/chamber-%s.png' % cssify(chamber.abbreviation))
return mark_safe(
u'<span class="chamber-icon" style="background-image: url({url})" '
u'title="{name}"></span>'.format(name=chamber.name, url=url))
u'<span class="chamber-icon" style="background-image: url({url})"'
u' data-toggle="tooltip" data-placement="{place}"'
u' title="{name}"></span>'.format(
name=chamber.name,
url=url,
place=tplace
)
)
@register.simple_tag
......@@ -138,7 +135,7 @@ def group_icon(group):
group.chamber.abbreviation, group.abbreviation)))