diff --git a/memopol/templatetags/__init__.py b/memopol/templatetags/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/memopol/templatetags/memopol_tags.py b/memopol/templatetags/memopol_tags.py new file mode 100644 index 0000000000000000000000000000000000000000..6c26fea64e55003f03b79646793ec30724dfc1f2 --- /dev/null +++ b/memopol/templatetags/memopol_tags.py @@ -0,0 +1,32 @@ +# coding: utf-8 +from django import template +from django.utils.safestring import mark_safe +import re + +register = template.Library() +link = '<a class="{network}-link" href="{url}" target="_blank">{label}</a>' + + +@register.filter +def twitter_link(url): + return mark_safe(link.format(network='twitter', url=url, + label=re.sub(r'.*/@?([^/]+)', '@\\1', re.sub(r'/$', '', url.strip())))) + + +@register.filter +def facebook_link(url): + return mark_safe(link.format(network='facebook', url=url, + label=re.sub(r'.*/([^/]+)', '\\1', re.sub(r'/$', '', url.strip())))) + + +@register.filter +def website_link(url): + short_url = re.sub(r'^https?://([^/]+).*', '\\1', url) + return mark_safe(link.format(network='website', url=url, + label=short_url)) + + +@register.filter +def email_link(address): + return mark_safe(link.format(network='email', url='mailto:%s' % address, + label=address)) diff --git a/memopol/tests/RepresentativeDetailTest/test_votes_display.html b/memopol/tests/RepresentativeDetailTest/test_votes_display.html index a87fb555481a12e2e2b2b8d348f74103a8a71634..5767ab55fb39ea689b1001a8328d0983d5a64396 100644 --- a/memopol/tests/RepresentativeDetailTest/test_votes_display.html +++ b/memopol/tests/RepresentativeDetailTest/test_votes_display.html @@ -3,6 +3,7 @@ <table class='table table-condensed votes'> <tr> <th>Title</th> + <th>Date</th> <th class='icon-cell'> Memopol recommendation </th> @@ -17,14 +18,15 @@ <tr> <td>Stop acta !</td> + <td class='date-cell'>Nov.24,2010</td> <td class='icon-cell'> <i aria-label="for" class="fa fa-thumbs-up vote_positive" title="for" ></i> </td> <td class='icon-cell'> - <i aria-label="for" class="fa fa-thumbs-up vote_positive" title="for" ></i> + <i aria-label="against" class="fa fa-thumbs-down vote_negative" title="against" ></i> </td> <td class='icon-cell'> - <span class="label label-success">4</span> + <span class="label label-danger">-6</span> </td> @@ -32,6 +34,7 @@ <tr> <td>Stop acta !</td> + <td class='date-cell'>Nov.24,2010</td> <td class='icon-cell'> <i aria-label="for" class="fa fa-thumbs-up vote_positive" title="for" ></i> </td> @@ -47,14 +50,15 @@ <tr> <td>Stop acta !</td> + <td class='date-cell'>Nov.24,2010</td> <td class='icon-cell'> <i aria-label="for" class="fa fa-thumbs-up vote_positive" title="for" ></i> </td> <td class='icon-cell'> - <i aria-label="against" class="fa fa-thumbs-down vote_negative" title="against" ></i> + <i aria-label="for" class="fa fa-thumbs-up vote_positive" title="for" ></i> </td> <td class='icon-cell'> - <span class="label label-danger">-6</span> + <span class="label label-success">4</span> </td> diff --git a/memopol/tests/response_fixtures/RepresentativeListTest.test_page1_paginateby12_displaylist/content b/memopol/tests/response_fixtures/RepresentativeListTest.test_page1_paginateby12_displaylist/content index 20cf6f9caba487f2f5bc914d2b13341ec14ed27a..67cb48d87d85ffec2e2b9861ba4d31fc23837e82 100644 --- a/memopol/tests/response_fixtures/RepresentativeListTest.test_page1_paginateby12_displaylist/content +++ b/memopol/tests/response_fixtures/RepresentativeListTest.test_page1_paginateby12_displaylist/content @@ -10,7 +10,7 @@ - The Political Memory of </title> - <link rel="stylesheet" href="/static/collected/libs/bootstrap/dist/css/bootstrap.min.css" type="text/css" /><link rel="stylesheet" href="/static/collected/libs/fontawesome/css/font-awesome.min.css" type="text/css" /><link rel="stylesheet" href="/static/collected/libs/flag-icon-css/css/flag-icon.min.css" type="text/css" /><link rel="stylesheet" href="/static/collected/CACHE/css/base.6aeeaa87703d.css" type="text/css" /> + <link rel="stylesheet" href="/static/collected/libs/bootstrap/dist/css/bootstrap.min.css" type="text/css" /><link rel="stylesheet" href="/static/collected/libs/fontawesome/css/font-awesome.min.css" type="text/css" /><link rel="stylesheet" href="/static/collected/libs/flag-icon-css/css/flag-icon.min.css" type="text/css" /><link rel="stylesheet" href="/static/collected/CACHE/css/base.3c5ffeab3cbf.css" type="text/css" /> <script type="text/javascript" src="/static/collected/libs/jquery/dist/jquery.js"></script> <script type="text/javascript" src="/static/collected/libs/bootstrap/dist/js/bootstrap.js"></script> diff --git a/memopol/tests/response_fixtures/RepresentativeListTest.test_page1_paginateby12_displaylist_searchjoly/content b/memopol/tests/response_fixtures/RepresentativeListTest.test_page1_paginateby12_displaylist_searchjoly/content index e7de597296e9b05d54adb41e60d32b69d6ed5d45..3e96d141906c1c12f8ad4e44126a6287890d5990 100644 --- a/memopol/tests/response_fixtures/RepresentativeListTest.test_page1_paginateby12_displaylist_searchjoly/content +++ b/memopol/tests/response_fixtures/RepresentativeListTest.test_page1_paginateby12_displaylist_searchjoly/content @@ -10,7 +10,7 @@ - The Political Memory of </title> - <link rel="stylesheet" href="/static/collected/libs/bootstrap/dist/css/bootstrap.min.css" type="text/css" /><link rel="stylesheet" href="/static/collected/libs/fontawesome/css/font-awesome.min.css" type="text/css" /><link rel="stylesheet" href="/static/collected/libs/flag-icon-css/css/flag-icon.min.css" type="text/css" /><link rel="stylesheet" href="/static/collected/CACHE/css/base.6aeeaa87703d.css" type="text/css" /> + <link rel="stylesheet" href="/static/collected/libs/bootstrap/dist/css/bootstrap.min.css" type="text/css" /><link rel="stylesheet" href="/static/collected/libs/fontawesome/css/font-awesome.min.css" type="text/css" /><link rel="stylesheet" href="/static/collected/libs/flag-icon-css/css/flag-icon.min.css" type="text/css" /><link rel="stylesheet" href="/static/collected/CACHE/css/base.3c5ffeab3cbf.css" type="text/css" /> <script type="text/javascript" src="/static/collected/libs/jquery/dist/jquery.js"></script> <script type="text/javascript" src="/static/collected/libs/bootstrap/dist/js/bootstrap.js"></script> diff --git a/memopol/tests/response_fixtures/RepresentativeListTest.test_page1_paginateby24_displaygrid/content b/memopol/tests/response_fixtures/RepresentativeListTest.test_page1_paginateby24_displaygrid/content index 9acdba75952ac3aa5c79e5207097fbebdd85a212..788d295dbbe65a2b338e98a18ba437a0b08b3e6e 100644 --- a/memopol/tests/response_fixtures/RepresentativeListTest.test_page1_paginateby24_displaygrid/content +++ b/memopol/tests/response_fixtures/RepresentativeListTest.test_page1_paginateby24_displaygrid/content @@ -10,7 +10,7 @@ - The Political Memory of </title> - <link rel="stylesheet" href="/static/collected/libs/bootstrap/dist/css/bootstrap.min.css" type="text/css" /><link rel="stylesheet" href="/static/collected/libs/fontawesome/css/font-awesome.min.css" type="text/css" /><link rel="stylesheet" href="/static/collected/libs/flag-icon-css/css/flag-icon.min.css" type="text/css" /><link rel="stylesheet" href="/static/collected/CACHE/css/base.6aeeaa87703d.css" type="text/css" /> + <link rel="stylesheet" href="/static/collected/libs/bootstrap/dist/css/bootstrap.min.css" type="text/css" /><link rel="stylesheet" href="/static/collected/libs/fontawesome/css/font-awesome.min.css" type="text/css" /><link rel="stylesheet" href="/static/collected/libs/flag-icon-css/css/flag-icon.min.css" type="text/css" /><link rel="stylesheet" href="/static/collected/CACHE/css/base.3c5ffeab3cbf.css" type="text/css" /> <script type="text/javascript" src="/static/collected/libs/jquery/dist/jquery.js"></script> <script type="text/javascript" src="/static/collected/libs/bootstrap/dist/js/bootstrap.js"></script> diff --git a/memopol/tests/response_fixtures/RepresentativeListTest.test_page2_paginateby12_displaylist/content b/memopol/tests/response_fixtures/RepresentativeListTest.test_page2_paginateby12_displaylist/content index 5f7c8f0944233325b18a0c529c1f909cdc6c5f2c..fbb2b4a8eeb275507d1a45242380901de4db14ad 100644 --- a/memopol/tests/response_fixtures/RepresentativeListTest.test_page2_paginateby12_displaylist/content +++ b/memopol/tests/response_fixtures/RepresentativeListTest.test_page2_paginateby12_displaylist/content @@ -10,7 +10,7 @@ - The Political Memory of </title> - <link rel="stylesheet" href="/static/collected/libs/bootstrap/dist/css/bootstrap.min.css" type="text/css" /><link rel="stylesheet" href="/static/collected/libs/fontawesome/css/font-awesome.min.css" type="text/css" /><link rel="stylesheet" href="/static/collected/libs/flag-icon-css/css/flag-icon.min.css" type="text/css" /><link rel="stylesheet" href="/static/collected/CACHE/css/base.6aeeaa87703d.css" type="text/css" /> + <link rel="stylesheet" href="/static/collected/libs/bootstrap/dist/css/bootstrap.min.css" type="text/css" /><link rel="stylesheet" href="/static/collected/libs/fontawesome/css/font-awesome.min.css" type="text/css" /><link rel="stylesheet" href="/static/collected/libs/flag-icon-css/css/flag-icon.min.css" type="text/css" /><link rel="stylesheet" href="/static/collected/CACHE/css/base.3c5ffeab3cbf.css" type="text/css" /> <script type="text/javascript" src="/static/collected/libs/jquery/dist/jquery.js"></script> <script type="text/javascript" src="/static/collected/libs/bootstrap/dist/js/bootstrap.js"></script> diff --git a/memopol/tests/response_fixtures/RepresentativeListTest.test_page2_paginateby24_displaylist/content b/memopol/tests/response_fixtures/RepresentativeListTest.test_page2_paginateby24_displaylist/content index 02e261dd653dc15655acc6d736390085f13c4cf9..f0e6eca85aa18d4ab8b8e3b2b7dee09b8b99fdd2 100644 --- a/memopol/tests/response_fixtures/RepresentativeListTest.test_page2_paginateby24_displaylist/content +++ b/memopol/tests/response_fixtures/RepresentativeListTest.test_page2_paginateby24_displaylist/content @@ -10,7 +10,7 @@ - The Political Memory of </title> - <link rel="stylesheet" href="/static/collected/libs/bootstrap/dist/css/bootstrap.min.css" type="text/css" /><link rel="stylesheet" href="/static/collected/libs/fontawesome/css/font-awesome.min.css" type="text/css" /><link rel="stylesheet" href="/static/collected/libs/flag-icon-css/css/flag-icon.min.css" type="text/css" /><link rel="stylesheet" href="/static/collected/CACHE/css/base.6aeeaa87703d.css" type="text/css" /> + <link rel="stylesheet" href="/static/collected/libs/bootstrap/dist/css/bootstrap.min.css" type="text/css" /><link rel="stylesheet" href="/static/collected/libs/fontawesome/css/font-awesome.min.css" type="text/css" /><link rel="stylesheet" href="/static/collected/libs/flag-icon-css/css/flag-icon.min.css" type="text/css" /><link rel="stylesheet" href="/static/collected/CACHE/css/base.3c5ffeab3cbf.css" type="text/css" /> <script type="text/javascript" src="/static/collected/libs/jquery/dist/jquery.js"></script> <script type="text/javascript" src="/static/collected/libs/bootstrap/dist/js/bootstrap.js"></script> diff --git a/memopol/tests/test_representatives_detail.py b/memopol/tests/test_representatives_detail.py index 5f29b3c606cf525bd02d17ddcc69f77b888d9755..6e98b67905e6963fcd1f94c6081478ea7aef2d91 100644 --- a/memopol/tests/test_representatives_detail.py +++ b/memopol/tests/test_representatives_detail.py @@ -12,9 +12,13 @@ class RepresentativeDetailTest(UrlGetTestMixin, TestCase): # Ensure one-time cached queries occur before the actual test self.client.get(self.url) - with self.assertNumQueries(5): + with self.assertNumQueries(10): """ - One query for the rep details and foreign key (profile) + - One query for reverse relation on phones + - One query for reverse relation on addresses + - One query for reverse relation on emails + - Two queries for reverse relation on websites (social and other) - One query for reverse relation on votes - One query for reverse relation on mandates - One query for reverse relation positions diff --git a/memopol/views.py b/memopol/views.py index 9e0c6e3dba284fd2843b33eecdc50faf97a3bc46..fb7ab10a090264685448591b0af087934bfeec0d 100644 --- a/memopol/views.py +++ b/memopol/views.py @@ -3,7 +3,7 @@ from django.db import models from core.views import GridListMixin, PaginationMixin, CSVDownloadMixin from representatives import views as representatives_views -from representatives.models import Representative +from representatives.models import (Representative, Address, Phone, WebSite) from representatives_votes import views as representatives_votes_views from representatives_votes.models import Dossier, Proposal from representatives_positions.forms import PositionForm @@ -41,11 +41,35 @@ class RepresentativeDetail(representatives_views.RepresentativeDetail): queryset = Representative.objects.select_related('score') def get_queryset(self): - qs = super(RepresentativeDetail, self).get_queryset() - votes = VoteScore.objects.filter( - proposal__in=Proposal.objects.exclude(recommendation=None), - ).select_related('proposal__recommendation') - qs = qs.prefetch_related(models.Prefetch('votes', queryset=votes)) + social = ['twitter', 'facebook'] + qs = super(RepresentativeDetail, self).get_queryset().prefetch_related( + 'email_set', + models.Prefetch( + 'website_set', + queryset=WebSite.objects.filter(kind__in=social), + to_attr='social_websites' + ), + models.Prefetch( + 'website_set', + queryset=WebSite.objects.exclude(kind__in=social), + to_attr='other_websites' + ), + models.Prefetch( + 'address_set', + queryset=Address.objects.select_related('country') + ), + models.Prefetch( + 'phone_set', + queryset=Phone.objects.select_related('address__country') + ), + models.Prefetch( + 'votes', + queryset=VoteScore.objects.filter( + proposal__in=Proposal.objects.exclude(recommendation=None), + ).select_related('proposal__recommendation').order_by( + '-proposal__datetime') + ) + ) return qs def get_context_data(self, **kwargs): @@ -53,6 +77,7 @@ class RepresentativeDetail(representatives_views.RepresentativeDetail): c['position_form'] = PositionForm( initial={'representative': self.object.pk}) self.add_representative_country_and_main_mandate(c['object']) + return c diff --git a/static/images/facebook.png b/static/images/facebook.png new file mode 100644 index 0000000000000000000000000000000000000000..e4eb58426c769666b1ba0ce7f002dfcd0d846ed0 Binary files /dev/null and b/static/images/facebook.png differ diff --git a/static/images/twitter.png b/static/images/twitter.png new file mode 100644 index 0000000000000000000000000000000000000000..7de53332db1ce2f7d51ffd1bed0837ab373b4e80 Binary files /dev/null and b/static/images/twitter.png differ diff --git a/static/less/base.less b/static/less/base.less index 214f4d142fa675637c94d5014abe22ba4e5533da..f8cff323c3006bca2ce5f360c464acaace53b4df 100644 --- a/static/less/base.less +++ b/static/less/base.less @@ -7,6 +7,7 @@ @import 'positions'; @import 'chambers'; @import 'groups'; +@import 'external'; body { background: #E5E5E5; diff --git a/static/less/external.less b/static/less/external.less new file mode 100644 index 0000000000000000000000000000000000000000..7e27fd5b8f2392e6d5269bd39d486d95ea108133 --- /dev/null +++ b/static/less/external.less @@ -0,0 +1,24 @@ +a.twitter-link:before, +a.facebook-link:before { + content: ' '; + overflow: hidden; + + display: inline-block; + width: 1.5em; + height: 1.5em; + + vertical-align: bottom; +} + +a.twitter-link:before { + background-size: 110%; + background-position: center; + background-image: url(../images/twitter.png); +} + +a.facebook-link:before { + background-size: 75%; + background-repeat: no-repeat; + background-position: center; + background-image: url(../images/facebook.png); +} \ No newline at end of file diff --git a/static/less/legislature.less b/static/less/legislature.less index 90791aca9dd2574709b073134f918e4adf3d2f3a..1c8c30db8262d9c1f04722c0010e4f786a122732 100644 --- a/static/less/legislature.less +++ b/static/less/legislature.less @@ -36,6 +36,10 @@ color: #333; } +.date-cell { + white-space: nowrap; +} + .representative_grid { display: flex; flex-wrap: wrap; diff --git a/templates/representatives/_address_block.haml b/templates/representatives/_address_block.haml new file mode 100644 index 0000000000000000000000000000000000000000..16eb4b4b5ae945cdbc29b7daab34b313fb5820ea --- /dev/null +++ b/templates/representatives/_address_block.haml @@ -0,0 +1,5 @@ +.address + - if address.floor and address.office_number + .office Floor {{ address.floor }} - office {{ address.office_number }} + .street {{ address.number }} {{ address.street }} + .city {{ address.city }} {{ address.postcode }}, {{ address.country.name }} diff --git a/templates/representatives/_representative_block.haml b/templates/representatives/_representative_block.haml index a02a9be782f4d76391457b09e04f48c526618d26..ca5797bfc955a15594563aeb1a86e3d2ca86d7de 100644 --- a/templates/representatives/_representative_block.haml +++ b/templates/representatives/_representative_block.haml @@ -1,3 +1,4 @@ +- load memopol_tags - load representatives_tags - load representatives_recommendations_tags - load humanize @@ -39,3 +40,20 @@ Born in {{ representative.birth_place }} the {{ representative.birth_date|naturalday:'d/m/Y' }} ({{ representative.get_gender_display }}) + + - if representative.social_websites|length > 0 + %tr + %th Social + %td + - for site in representative.social_websites + - if site.kind == 'twitter' + = site.url|twitter_link + - elif site.kind == 'facebook' + = site.url|facebook_link + + - if representative.other_websites|length > 0 + %tr + %th Websites + %td + - for site in representative.other_websites + = site.url|website_link diff --git a/templates/representatives/_representative_contact.haml b/templates/representatives/_representative_contact.haml new file mode 100644 index 0000000000000000000000000000000000000000..4be0c1f602f33d9040eb1fe7ffbe64192faa01b1 --- /dev/null +++ b/templates/representatives/_representative_contact.haml @@ -0,0 +1,25 @@ +- load memopol_tags + +.row.representative-contact + .col-md-12 + %h2 Contact information + %table.table.table-condensed.detail-view + - for email in representative.email_set.all + %tr + %th E-mail + %td + = email.email|email_link + + - if representative.phone_set.all|length > 0 + %tr + %th Phone numbers + %td + - for phone in representative.phone_set.all + .phone {{ phone.number }} + + - for address in representative.address_set.all + %tr + %th + = address.name + %td + - include 'representatives/_address_block.html' with address=address diff --git a/templates/representatives/representative_detail.haml b/templates/representatives/representative_detail.haml index 94629ffda1bc816dceb3cb36c4007f2b58942510..8d3214cd1112374c29d72746aa99270f4002a036 100644 --- a/templates/representatives/representative_detail.haml +++ b/templates/representatives/representative_detail.haml @@ -12,6 +12,7 @@ .row .col-md-6 - include 'representatives/_representative_block.html' with representative=representative + - include 'representatives/_representative_contact.html' with representative=representative .col-md-6 %h2#votes Votes @@ -19,6 +20,7 @@ %table.table.table-condensed.votes %tr %th Title + %th Date %th.icon-cell Memopol recommendation %th.icon-cell @@ -29,6 +31,8 @@ - for vote in votes %tr %td= vote.proposal.recommendation.title + %td.date-cell + = vote.proposal.datetime|date:'N j, Y' %td.icon-cell = vote.proposal.recommendation.recommendation|position_icon %td.icon-cell