Commit ba69e530 authored by njoyard's avatar njoyard

Merge branch 'homepage-additions' into 'master'

Homepage additions

- Today's mep : random mep (seeded by current date) chosen from meps with nonzero score

See merge request !169
parents e23a4a2e 168915a2
from memopol_settings.models import Setting
from representatives.models import Chamber, Group
......@@ -13,3 +14,13 @@ def search_form_options(request):
d['committees'] = Group.objects.filter(kind='committee')
return d
def intro_text(request):
d = {}
for s in Setting.objects.filter(pk__in=['HOMEPAGE_INTRO_TEXT',
'HOMEPAGE_INSTANCE_TEXT']):
d[s.pk] = s.comment
return d
......@@ -99,6 +99,10 @@ MIDDLEWARE_CLASSES = (
'django.contrib.sites.middleware.CurrentSiteMiddleware',
)
if 'debug_toolbar' in INSTALLED_APPS:
MIDDLEWARE_CLASSES += (
'debug_toolbar.middleware.DebugToolbarMiddleware',
)
#
# Sessions
#
......@@ -130,6 +134,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.intro_text'
)
#
......
......@@ -92,7 +92,6 @@ a.custom-invisible, a.custom-invisible:hover, a.custom-invisible:focus {
}
/***************************************************************
Forms
***************************************************************/
......@@ -261,7 +260,7 @@ a:hover .custom-thumbnail-details {
position: relative;
}
.custom-listMEP .thumbnail .badge,
.representative-card .thumbnail .badge,
.position-button .badge {
position: absolute;
top: -0.5em;
......@@ -333,6 +332,12 @@ iframe {
background-size: cover;
}
.todays-mep .representative-card .img-responsive {
width: 150px;
height: 150px;
}
.representative-card h4 {
margin-top: 15px;
}
......@@ -365,6 +370,44 @@ iframe {
right: 0.5em;
}
/***************************************************************
Themes
***************************************************************/
.theme-card .description {
margin: 0 1em;
line-height: 1.2em;
}
.theme-card .info-container {
display: flex;
flex-flow: row nowrap;
padding: 1em;
}
.theme-card .badge-container {
display: flex;
flex-flow: column nowrap;
justify-content: center;
}
.theme-card .badge-container .label {
margin: .25em 0;
}
.theme-card .chart-container {
flex-grow: 1;
height: 125px;
margin-left: .5em;
border: 1px solid #ddd;
background-image: url(../images/logo.png);
background-size: contain;
background-position: center;
background-repeat: no-repeat;
}
/***************************************************************
Dossiers
***************************************************************/
......@@ -401,6 +444,10 @@ iframe {
margin-left: 1em;
}
.proposal-details, .proposal-details td {
border: 0 !important;
}
/***************************************************************
Icones
***************************************************************/
......@@ -438,6 +485,7 @@ iframe {
margin-left: .5em;
}
.vote_positive {
color: green;
}
......
......@@ -12,7 +12,7 @@
</h1>
<p class="lead text-center hidden-xs">{% trans "What is Memopol ?" %}</p>
<p class="text-justify hidden-xs">
{% include "text/short_description.html" %}
{{ HOMEPAGE_INTRO_TEXT }}
</p>
{% if position_form %}
<div class="container-fluid hidden-xs">
......
......@@ -3,6 +3,7 @@
{% load i18n %}
{% load fontawesome %}
{% load staticfiles %}
{% load memopol_tags %}
{% block content %}
<div class="row">
......@@ -13,14 +14,76 @@
<div class="row">
<div class="col-md-12">
<h2>{% trans "What is Memopol ?" %}</h2>
<p>
{% include "text/short_description.html" %}
{{ HOMEPAGE_INTRO_TEXT }}
</p>
<h2>{% trans "What about this instance ?" %}</h2>
<p>
{% include "text/license.html" %}
{{ HOMEPAGE_INSTANCE_TEXT }}
</p>
</div>
</div>
<div class="row">
<div class="col-md-4">
<h2>{% trans "Latest votes" %}</h2>
<table class="table table-responsive table-condensed">
<tr>
<th></th>
<th></th>
<th></th>
<th>{{ "for"|position_icon }}</th>
<th>{{ "against"|position_icon }}</th>
<th>{{ "abstain"|position_icon }}</th>
</tr>
{% for proposal in latest_votes %}
<tr class="proposal-title {% if forloop.counter0|divisibleby:2 %}active{% endif %}">
<td colspan="6">
<a href="{% url 'dossier-detail' proposal.dossier.pk %}">
{{ proposal.title }}
</a>
</td>
</tr>
<tr class="proposal-details {% if forloop.counter0|divisibleby:2 %}active{% endif %}">
<td>
{% for chamber in proposal.dossier.chambers %}
{{ chamber | chamber_icon }}
{% endfor %}
</td>
<td>
{% include "blocks/_themetags.html" with themes=proposal|proposal_themes %}
</td>
<td>{{ proposal.status|proposal_status_label:proposal.recommendation }}</td>
<td>{{ proposal.total_for }}</td>
<td>{{ proposal.total_against }}</td>
<td>{{ proposal.total_abstain }}</td>
</tr>
{% endfor %}
</table>
</div>
<div class="col-md-4">
<h2>{% trans "Featured themes" %}</h2>
{% for theme in featured_themes %}
{% include "memopol_themes/_theme_card.html" with theme=theme cols=12 %}
{% empty %}
{% trans "No currently featured theme :'(" %}
{% endfor %}
</div>
<div class="col-md-4 todays-mep">
<h2>{% trans "Today's Representative" %}</h2>
{% include "representatives/_representative_card.html" with representative=todays_mep cols=12 %}
</div>
</div>
<div class="row">
<div class="col-md-4">
<h3>{% trans "More information" %}</h3>
<div class="btn-group">
......@@ -64,4 +127,14 @@
</div>
</div>
<div class="row">
<div class="col-md-12">
<p>
<br><br>
{% include "text/license.html" %}
<br><br>
</p>
</div>
</div>
{% endblock %}
{% load i18n %}
{% load fontawesome %}
<div class="col-xs-12 col-md-{{ cols|default:4 }} theme-card">
<div class="thumbnail">
<a href="{% url 'theme-detail' slug=theme.slug %}" class="custom-thumbnail custom-invisible">
<div class="row">
<div class="col-xs-12">
<h4 class="text-center">{{ theme.name }}</h4>
{# Double div needed for ellipsis to work #}
<div class="description ellipsis"><div>{{ theme.description|truncatewords:20 }}</div></div>
<div class="info-container">
<div class="badge-container">
{% if theme.nb_links > 0 %}
<span class="label label-default" data-toggle="tooltip" data-placement="bottom" title="{% trans 'Links' %}">
{% fontawesome_icon "link" %}
<span class="badge">{{ theme.nb_links }}</span>
</span>
{% endif %}
{% if theme.nb_dossiers > 0 %}
<span class="label label-default" data-toggle="tooltip" data-placement="bottom" title="{% trans 'Dossiers' %}">
{% fontawesome_icon "book" %}
<span class="badge">{{ theme.nb_dossiers }}</span>
</span>
{% endif %}
{% if theme.nb_proposals > 0 %}
<span class="label label-default" data-toggle="tooltip" data-placement="bottom" title="{% trans 'Proposals' %}">
{% fontawesome_icon "pencil" %}
<span class="badge">{{ theme.nb_proposals }}</span>
</span>
{% endif %}
{% if theme.nb_positions > 0 %}
<span class="label label-default" data-toggle="tooltip" data-placement="bottom" title="{% trans 'Public positions' %}">
{% fontawesome_icon "comment" %}
<span class="badge">{{ theme.nb_positions }}</span>
</span>
{% endif %}
</div>
<div class="chart-container">
</div>
</div>
</div>
</div>
</a>
</div>
</div>
\ No newline at end of file
......@@ -16,52 +16,7 @@
<div class="row card-list">
{% for theme in object_list %}
<div class="col-xs-12 col-md-4 theme-card">
<div class="thumbnail">
<a href="{% url 'theme-detail' slug=theme.slug %}" class="custom-thumbnail custom-invisible">
<div class="row">
<div class="col-xs-12">
<h4 class="text-center">{{ theme.name }}</h4>
<p class="text-center lead">
&nbsp;
{% if theme.nb_links > 0 %}
<span class="label label-default" data-toggle="tooltip" data-placement="bottom" title="{% trans 'Links' %}">
{% fontawesome_icon "link" %}
<span class="badge">{{ theme.nb_links }}</span>
</span>
{% endif %}
{% if theme.nb_dossiers > 0 %}
<span class="label label-default" data-toggle="tooltip" data-placement="bottom" title="{% trans 'Dossiers' %}">
{% fontawesome_icon "book" %}
<span class="badge">{{ theme.nb_dossiers }}</span>
</span>
{% endif %}
{% if theme.nb_proposals > 0 %}
<span class="label label-default" data-toggle="tooltip" data-placement="bottom" title="{% trans 'Proposals' %}">
{% fontawesome_icon "pencil" %}
<span class="badge">{{ theme.nb_proposals }}</span>
</span>
{% endif %}
{% if theme.nb_positions > 0 %}
<span class="label label-default" data-toggle="tooltip" data-placement="bottom" title="{% trans 'Public positions' %}">
{% fontawesome_icon "comment" %}
<span class="badge">{{ theme.nb_positions }}</span>
</span>
{% endif %}
&nbsp;
</p>
</div>
</div>
</a>
</div>
</div>
{% include "memopol_themes/_theme_card.html" with theme=theme %}
{% endfor %}
</div>
......
{% load memopol_tags %}
<div class="col-xs-12 col-md-{{ cols|default:4 }} representative-card">
<div class="thumbnail">
<a href="{% url 'representative-detail' slug=representative.slug %}" class="custom-thumbnail custom-invisible">
<div class="row">
<div class="col-xs-5">
<img src="data:image/gif;base64,R0lGODlhAQABAIAAAP///////yH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" class="img-responsive" style="background-image: url({{ representative.photo }});">
<div class="custom-thumbnail-details">
{% if representative.country %}
<div class="icon-badge-detail">{{ representative.country|country_flag }} {{ representative.country.name }}</div>
{% endif %}
{% if representative.chamber %}
<div class="icon-badge-detail">{{ representative.chamber|chamber_icon }} {{ representative.chamber.name }}</div>
{% endif %}
{% if representative.main_mandate.group %}
<div class="icon-badge-detail">{{ representative.main_mandate.group|group_icon }} {{ representative.main_mandate.group.abbreviation }}</div>
{% endif %}
</div>
</div>
<div class="col-xs-7">
<h4 class="text-center">{{ representative.full_name }}</h4>
<p class="text-center">
<br>
{% if representative.country %}
{{ representative.country|country_flag }}
{% endif %}
{% if representative.chamber %}
{{ representative.chamber|chamber_icon }}
{% endif %}
{% if representative.main_mandate.group %}
{{ representative.main_mandate.group|group_icon }}
{% endif %}
</p>
<p class="text-right">{{ representative.representative_score.score|score_badge }}</p>
</div>
</div>
</a>
</div>
</div>
\ No newline at end of file
{% extends 'base.html' %}
{% load i18n %}
{% load memopol_tags %}
{% block title %}{% trans "Members of the European Parliement" %}{% endblock %}
......@@ -15,47 +14,8 @@
{% include "blocks/listheader.html" %}
<div class="row card-list">
{% for representative in object_list %}
<div class="col-xs-12 col-md-4 representative-card">
<div class="thumbnail">
<a href="{% url 'representative-detail' slug=representative.slug %}" class="custom-thumbnail custom-invisible">
<div class="row">
<div class="col-xs-5">
<img src="data:image/gif;base64,R0lGODlhAQABAIAAAP///////yH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" class="img-responsive" style="background-image: url({{ representative.photo }});">
<div class="custom-thumbnail-details">
{% if representative.country %}
<div class="icon-badge-detail">{{ representative.country|country_flag }} {{ representative.country.name }}</div>
{% endif %}
{% if representative.chamber %}
<div class="icon-badge-detail">{{ representative.chamber|chamber_icon }} {{ representative.chamber.name }}</div>
{% endif %}
{% if representative.main_mandate.group %}
<div class="icon-badge-detail">{{ representative.main_mandate.group|group_icon }} {{ representative.main_mandate.group.abbreviation }}</div>
{% endif %}
</div>
</div>
<div class="col-xs-7">
<h4 class="text-center">{{ representative.full_name }}</h4>
<p class="text-center">
<br>
{% if representative.country %}
{{ representative.country|country_flag }}
{% endif %}
{% if representative.chamber %}
{{ representative.chamber|chamber_icon }}
{% endif %}
{% if representative.main_mandate.group %}
{{ representative.main_mandate.group|group_icon }}
{% endif %}
</p>
<p class="text-right">{{ representative.representative_score.score|score_badge }}</p>
</div>
</div>
</a>
</div>
</div>
{% for mep in object_list %}
{% include "representatives/_representative_card.html" with representative=mep cols=4 %}
{% empty %}
{% trans "No representatives found !" %}
{% endfor %}
......
{% load i18n %}
{% blocktrans %}
Political Memory is a tool designed by La Quadrature du Net to help
citizens to reach their representatives and
track their voting records on issues related to fundamental
freedoms online.
{% endblocktrans %}
......@@ -118,3 +118,8 @@ def score_badge(score, tooltip=None):
@register.filter
def cast_str(val):
return str(val)
@register.filter
def proposal_themes(proposal):
return set(proposal.themes.all()) | set(proposal.dossier.themes.all())
......@@ -8,6 +8,7 @@ class BaseTest(ResponseDiffTestMixin, test.TestCase):
"""
Common queries
- 1 for settings
- 5 for search forms
- 1 for chambers
- 1 for countries
......@@ -18,7 +19,7 @@ class BaseTest(ResponseDiffTestMixin, test.TestCase):
- 1 for representatives
- 1 for themes
"""
left_pane_queries = 7
left_pane_queries = 8
def setUp(self):
RepresentativeScore.refresh()
......
<div class="col-xs-12 col-md-4 theme-card">
<div class="thumbnail">
<a class="custom-thumbnail custom-invisible" href="/themes/acta/">
<div class="row">
<div class="thumbnail">
<a class="custom-thumbnail custom-invisible" href="/themes/acta/">
<div class="row">
<div class="col-xs-12">
<h4 class="text-center">ACTA</h4>
<div class="col-xs-12">
<h4 class="text-center">ACTA</h4>
<p class="text-center lead">
 
<div class="description ellipsis"><div>Anti-Counterfeiting Trade Agreement</div></div>
<div class="info-container">
<div class="badge-container">
<span class="label label-default" data-placement="bottom" data-toggle="tooltip" title="Dossiers">
<i class="fa fa-book" title=""></i>
<span class="badge">3</span>
</span>
<span class="label label-default" data-placement="bottom" data-toggle="tooltip" title="Dossiers">
<i class="fa fa-book" title=""></i>
<span class="badge">3</span>
</span>
 
</p>
</div>
<div class="chart-container">
</div>
</div>
</a>
</div>
</div>
</a>
</div>
</div>
---
<div class="col-xs-12 col-md-4 theme-card">
<div class="thumbnail">
<a class="custom-thumbnail custom-invisible" href="/themes/etat-durgence/">
<div class="row">
<div class="col-xs-12">
<h4 class="text-center">Etat d'urgence</h4>
<div class="thumbnail">
<a class="custom-thumbnail custom-invisible" href="/themes/etat-durgence/">
<div class="row">
<p class="text-center lead">
 
<div class="col-xs-12">
<h4 class="text-center">Etat d'urgence</h4>
<span class="label label-default" data-placement="bottom" data-toggle="tooltip" title="Links">
<i class="fa fa-link" title=""></i>
<span class="badge">2</span>
</span>
<div class="description ellipsis"><div>La réponse sécuritaire n'est pas la solution.</div></div>
<span class="label label-default" data-placement="bottom" data-toggle="tooltip" title="Dossiers">
<i class="fa fa-book" title=""></i>
<span class="badge">4</span>
</span>
<div class="info-container">
<div class="badge-container">
<span class="label label-default" data-placement="bottom" data-toggle="tooltip" title="Links">
<i class="fa fa-link" title=""></i>
<span class="badge">2</span>
</span>
<span class="label label-default" data-placement="bottom" data-toggle="tooltip" title="Proposals">
<i class="fa fa-pencil" title=""></i>
<span class="badge">13</span>
</span>
<span class="label label-default" data-placement="bottom" data-toggle="tooltip" title="Dossiers">
<i class="fa fa-book" title=""></i>
<span class="badge">4</span>
</span>
<span class="label label-default" data-placement="bottom" data-toggle="tooltip" title="Proposals">
<i class="fa fa-pencil" title=""></i>
<span class="badge">13</span>
</span>
 
</p>
</div>
<div class="chart-container">
</div>
</div>
</a>
</div>
</div>
\ No newline at end of file
</div>
</a>
</div>
</div>
\ No newline at end of file
......@@ -8,7 +8,24 @@ class NavigationPaneTest(BaseTest):
# First query to set session variables
self.client.get(self.url)
with self.assertNumQueries(self.left_pane_queries):
"""
Today mep
- 1 for count reps with non null score
- 1 for random mep
- 1 for prefetch main mandate
Latest votes
- 1 for latest votes count setting
- 1 for latest votes (proposal)
- 1 for prefetching latest votes themes
- 1 for prefetching latest votes dossier themes
- 1 for prefetching latest votes dossier documents
- 1 for prefetching latest votes dossier documents chambers
Featured themes
- 1 for featured themes
"""
home_queries = 10
with self.assertNumQueries(self.left_pane_queries + home_queries):
self.client.get(self.url)
def test_rep_search_chambers(self):
......
# coding: utf-8
import datetime
import random
from django.db.models import Q, Count
from django.views import generic
from representatives.models import Representative
from representatives_positions.views import PositionFormMixin
from representatives_votes.models import Proposal
from memopol_settings.models import Setting
from memopol_themes.models import Theme
from .representative_mixin import RepresentativeViewMixin
class HomeView(PositionFormMixin, generic.TemplateView):
class HomeView(PositionFormMixin, RepresentativeViewMixin,
generic.TemplateView):
template_name = 'home.html'
def get_context_data(self, **kwargs):
c = super(HomeView, self).get_context_data(**kwargs)
# Today's mep
qs = Representative.objects
qs = qs.filter(Q(representative_score__score__lt=0) |
Q(representative_score__score__gt=0))
qs = self.prefetch_for_representative_country_and_main_mandate(qs)
qs = qs.select_related('representative_score')
random.seed(datetime.date.today().isoformat())
index = random.randint(0, qs.count() - 1)
c['todays_mep'] = qs.all()[index]
self.add_representative_country_and_main_mandate(c['todays_mep'])
# Featured themes
c['featured_themes'] = Theme.objects \
.filter(featured=True) \
.annotate(
nb_links=Count('links', distinct=True),
nb_dossiers=Count('dossiers', distinct=True),
nb_proposals=Count('proposals', distinct=True),
nb_positions=Count('positions', distinct=True))
# Last votes
num = int(Setting.objects.get(pk='HOMEPAGE_LATEST_VOTES').value)
c['latest_votes'] = Proposal.objects \
.filter(recommendation__isnull=False) \
.select_related('dossier', 'recommendation') \
.prefetch_related('themes', 'dossier__themes',
'dossier__documents__chamber') \
.order_by('-datetime')[0:num]
return c
[
{
"model": "memopol_settings.setting",
"pk": "HOMEPAGE_LATEST_VOTES",
"fields": {
"value": "5",
"comment": "Number of 'latest votes' to show on the homepage."
}
},
{
"model": "memopol_settings.setting",
"pk": "HOMEPAGE_INTRO_TEXT",
"fields": {
"value": "** Use comment field for this setting **",
"comment": "Political Memory is a tool to help citizens reach their representatives and track their voting records."
}
},
{
"model": "memopol_settings.setting",
"pk": "HOMEPAGE_INSTANCE_TEXT",
"fields": {
"value": "** Use comment field for this setting **",
"comment": "This instance of Memopol is run by La Quadrature du Net and tracks issues related to fundamental freedoms online."
}
}