Commit 9c884e2a authored by James Pic's avatar James Pic
Browse files

Merge pull request #47 from political-memory/pr

Release
parents de0af789 832e7e33
# coding: utf-8
# This file is part of memopol.
#
# memopol is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of
# the License, or any later version.
#
# memopol is distributed in the hope that it will
# be useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU General Affero Public
# License along with django-representatives.
# If not, see <http://www.gnu.org/licenses/>.
#
# Copyright (C) 2015 Arnaud Fabre <af@laquadrature.net>
from datetime import datetime
from django.http import Http404
from django.shortcuts import render, get_object_or_404
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.db.models import Q
from representatives.models import Group
from legislature.models import MemopolRepresentative
def retrieve(request, pk=None, name=None):
if pk:
representative = get_object_or_404(
MemopolRepresentative,
id=pk
)
elif name:
representative = get_object_or_404(
MemopolRepresentative,
full_name=name
)
else:
return Http404()
return render(
request,
'legislature/representative_view.html',
{'representative': representative}
)
def representatives_by_group(request, group_kind, group_abbr=None,
group_name=None, search=None, group_id=None):
if group_id:
representative_list = MemopolRepresentative.objects.filter(
mandates__group_id=group_id,
mandates__end_date__gte=datetime.now()
)
elif group_abbr:
representative_list = MemopolRepresentative.objects.filter(
mandates__group__abbreviation=group_abbr,
mandates__group__kind=group_kind,
mandates__end_date__gte = datetime.now()
)
elif group_name:
representative_list = MemopolRepresentative.objects.filter(
Q(mandates__group__name__icontains=group_name),
mandates__group__kind=group_kind,
mandates__end_date__gte = datetime.now()
)
elif search:
try:
Group.objects.get(abbreviation=search, kind=group_kind)
representative_list = MemopolRepresentative.objects.filter(
mandates__group__abbreviation=search,
mandates__group__kind=group_kind,
mandates__end_date__gte = datetime.now()
)
except Group.DoesNotExist:
representative_list = MemopolRepresentative.objects.filter(
Q(mandates__group__abbreviation__icontains=search) |
Q(mandates__group__name__icontains=search),
mandates__group__kind=group_kind,
mandates__end_date__gte = datetime.now()
)
# Select distinct representatives and filter by search
representative_list = list(set(
_filter_by_search(request, representative_list)
))
return _render_list(request, representative_list)
def _filter_by_search(request, representative_list):
"""
Return a representative_list filtered by
the representative name provided in search form
"""
search = request.GET.get('search')
if search:
return representative_list.filter(
Q(full_name__icontains=search)
)
else:
return representative_list
def _render_list(request, representative_list, num_by_page=30):
"""
Render a paginated list of representatives
"""
paginator = Paginator(representative_list, num_by_page)
page = request.GET.get('page')
try:
representatives = paginator.page(page)
except PageNotAnInteger:
representatives = paginator.page(1)
except EmptyPage:
representatives = paginator.page(paginator.num_pages)
context = {}
queries_without_page = request.GET.copy()
if 'page' in queries_without_page:
del queries_without_page['page']
context['queries'] = queries_without_page
context['representatives'] = representatives
context['representative_num'] = paginator.count
return render(
request,
'legislature/representative_list.html',
context
)
def groups_by_kind(request, kind):
groups = Group.objects.filter(
kind=kind,
mandates__end_date__gte=datetime.now()
).distinct().order_by('name')
return render(
request,
'legislature/groups_list.html',
{'groups': groups}
)
# coding: utf-8
# This file is part of memopol.
#
# memopol is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of
# the License, or any later version.
#
# memopol is distributed in the hope that it will
# be useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU General Affero Public
# License along with django-representatives.
# If not, see <http://www.gnu.org/licenses/>.
#
# Copyright (C) 2015 Arnaud Fabre <af@laquadrature.net>
from __future__ import absolute_import
from celery import shared_task
from .models import MemopolRepresentative
@shared_task
def representatives_update_all():
'''
Call MemopolRepresentative.update_all methods
'''
for representative in MemopolRepresentative.objects.all():
representative.update_all()
{% extends "base.html" %}
{% block content %}
<form action="" method="get">
{{ filter.form.as_p }}
<input type="submit" />
</form>
{% for obj in filter %}
{{ obj.name }} - ${{ obj.price }}<br />
{% endfor %}
{% endblock %}
- extends 'base.html'
- load representatives_extras
- block content
- include 'legislature/search.html'
- include 'core/blocks/pagination.html'
%table.table
%tr
%th
Photo
%th
Name
%th
Country
%th
Group
%th
Score
- for representative in object_list
%tr
%td
%a{'href': "{% url 'legislature:representative-detail' name=representative.slug %}"}
%img{'src': '={representative.photo}', 'width': '80'}/
%td
%a{'href': "{% url 'legislature:representative-detail' name=representative.slug %}"}
= representative.full_name
%td
%a{'href': "{% url 'legislature:representative-index' group_kind='country' group=representative.country.code %}"}
= representative.country|country_flag
%td
%a{'href': "{{ representative.main_mandate|by_group_url }}"}
= representative.main_mandate.group.abbreviation
%td
= representative.score|score_label
- include "core/blocks/pagination.html"
<form action="" method="get">
{% csrf_token %}
<label for="search">Search : </label>
<input id="search" type="text" name="search">
<input type="submit" value="Go">
</form>
# coding: utf-8
# This file is part of memopol.
#
# memopol is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of
# the License, or any later version.
#
# memopol is distributed in the hope that it will
# be useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU General Affero Public
# License along with django-representatives.
# If not, see <http://www.gnu.org/licenses/>.
#
# Copyright (C) 2015 Arnaud Fabre <af@laquadrature.net>
"""
This file contains all templatetags used by the representative app
"""
from django import template
from django.core.urlresolvers import reverse
from django.utils.safestring import mark_safe
from django.contrib.humanize.templatetags.humanize import naturalday
from representatives.models import Mandate, Group
register = template.Library()
@register.filter
def mandate_date(date, arg=None):
if date.year == 9999:
return 'present'
else:
return naturalday(date, arg)
@register.filter
def position_icon(position):
if position == 'for':
return mark_safe(
'<i \
aria-label="for" \
class="fa fa-thumbs-up vote_positive" \
title="for" \
></i>')
elif position == 'against':
return mark_safe(
'<i \
aria-label="against" \
class="fa fa-thumbs-down vote_negative" \
title="against" \
></i>')
else:
return mark_safe(
'<i \
aria-label="abstain" \
class="fa fa-circle-o vote_abstain" \
title="abstain" \
></i>')
@register.filter
def score_label(score):
if score > 0:
return mark_safe('<span class="label label-success">{}</span>'.format(score))
elif score < 0:
return mark_safe('<span class="label label-danger">{}</span>'.format(score))
else:
return mark_safe('<span class="label label-default">{}</span>'.format(score))
@register.filter
def country_flag(country):
return mark_safe('<span class="flag-icon flag-icon-{code}"></span> {name}'.format(
name=country.name,
code=country.code.lower()
))
@register.filter
def by_group_url(group):
if isinstance(group, Mandate):
group = group.group
if not isinstance(group, Group):
return ''
kwargs = {'group_kind': group.kind}
if group.abbreviation:
kwargs['group'] = group.abbreviation
else:
kwargs['group'] = group.name
# kwargs['group_id'] = group.id
return reverse(
'legislature:representative-index',
kwargs=kwargs
)
from django.test import TestCase
# Create your tests here.
# coding: utf-8
# This file is part of memopol.
#
# memopol is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of
# the License, or any later version.
#
# memopol is distributed in the hope that it will
# be useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU General Affero Public
# License along with django-representatives.
# If not, see <http://www.gnu.org/licenses/>.
#
# Copyright (C) 2015 Arnaud Fabre <af@laquadrature.net>
from __future__ import absolute_import
from django.conf.urls import url
from .views import representative
from .views import group
urlpatterns = [
# List of groups by group kind
url(
r'^groups/(?P<kind>\w+)?$',
group.index,
name='group-index'
),
# Representative detail by representative name
url(
r'^(?P<name>[-\w]+)$',
representative.detail,
name='representative-detail'
),
# Representative detail by representative pk
url(
r'^(?P<pk>\d+)$',
representative.detail,
name='representative-detail'
),
# List of representatives by group kind and group name or pk
url(
r'^(?P<group_kind>\w+)/(?P<group>.+)$',
representative.index,
name='representative-index'
),
# List all representatives by default
url(
r'',
representative.index,
name='representative-index'
),
]
# coding: utf-8
# This file is part of memopol.
#
# memopol is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of
# the License, or any later version.
#
# memopol is distributed in the hope that it will
# be useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU General Affero Public
# License along with django-representatives.
# If not, see <http://www.gnu.org/licenses/>.
#
# Copyright (C) 2015 Arnaud Fabre <af@laquadrature.net>
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.shortcuts import render
def render_paginate_list(request, object_list, template_name, num_by_page=30):
"""
Render a paginated list of representatives
"""
paginator = Paginator(object_list, num_by_page)
page = request.GET.get('page')
try:
objects = paginator.page(page)
except PageNotAnInteger:
objects = paginator.page(1)
except EmptyPage:
objects = paginator.page(paginator.num_pages)
context = {}
queries_without_page = request.GET.copy()
if 'page' in queries_without_page:
del queries_without_page['page']
context['queries'] = queries_without_page
context['object_list'] = objects
context['object_count'] = paginator.count
return render(
request,
template_name,
context
)
# coding: utf-8
# This file is part of memopol.
#
# memopol is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of
# the License, or any later version.
#
# memopol is distributed in the hope that it will
# be useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU General Affero Public
# License along with django-representatives.
# If not, see <http://www.gnu.org/licenses/>.
#
# Copyright (C) 2015 Arnaud Fabre <af@laquadrature.net>
from datetime import datetime
from django.shortcuts import render
from representatives.models import Group
def index(request, kind=None):
groups = Group.objects.filter(
mandates__end_date__gte=datetime.now()
)
if kind:
groups = groups.filter(
kind=kind
)
groups = groups.distinct().order_by('name')
return render(
request,
'legislature/groups_list.html',
{'groups': groups}
)
# coding: utf-8
# This file is part of memopol.
#
# memopol is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of
# the License, or any later version.
#
# memopol is distributed in the hope that it will
# be useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU General Affero Public
# License along with django-representatives.
# If not, see <http://www.gnu.org/licenses/>.
#
# Copyright (C) 2015 Arnaud Fabre <af@laquadrature.net>
from __future__ import absolute_import
from datetime import datetime
from django.shortcuts import render
from django.db.models import Q
from django.http import Http404
from ..models import MemopolRepresentative
from ..filters import RepresentativeFilter
from core.utils import render_paginate_list
from positions.forms import PositionForm
def index(request, group_kind=None, group=None):
# Fetch active representatives
representative_list = MemopolRepresentative.objects.select_related(
'country',
'main_mandate',
'main_mandate__group',
).filter(
active=True
)
# Filter the list by group if group information is provided
if group_kind:
if group.isnumeric():
representative_list = representative_list.filter(
mandates__group_id=int(group),
mandates__end_date__gte=datetime.now()
)
else:
# Search group based on abbreviation or name
representative_list = representative_list.filter(
Q(mandates__group__abbreviation=group) |
Q(mandates__group__name=group),
mandates__group__kind=group_kind,
mandates__end_date__gte=datetime.now()
)
# Filter the list by search
representative_list = _filter_by_search(
request,
representative_list
).order_by('-score', 'last_name')
# Grid or list
if request.GET.get('display') in ('grid', 'list'):
request.session['display'] = request.GET.get('display')
if not 'display' in request.session:
request.session['display'] = 'grid'
# representative_list = RepresentativeFilter(request.GET, queryset=representative_list)
# Render the paginated template
return render_paginate_list(
request,
representative_list,
'legislature/representative_{}.html'.format(
request.session['display']
)
)
def detail(request, pk=None, name=None):
try:
query_set = MemopolRepresentative.objects.select_related(
'country',
'main_mandate'
)
if pk:
representative = query_set.get(
id=pk
)
elif name:
representative = query_set.get(
slug=name
)
else:
return Http404()
except MemopolRepresentative.DoesNotExist:
return Http404()
position_form = PositionForm()
return render(
request,
'legislature/representative_detail.html',
{
'representative': representative,