Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
Anthony
memopol
Commits
377706f9
Commit
377706f9
authored
Aug 19, 2016
by
Nicolas Joyard
Browse files
Implement theme list
parent
a831f374
Changes
15
Hide whitespace changes
Inline
Side-by-side
memopol/fixtures/smaller_sample.json
View file @
377706f9
...
...
@@ -23213,11 +23213,26 @@
28147
],
"slug": "acta",
"name": "
acta
"
"name": "
ACTA
"
},
"model": "memopol_themes.theme",
"pk": 1
},
{
"fields": {
"description": "None yet.",
"positions": [
],
"proposals": [
],
"dossiers": [
],
"slug": "etat-durgence",
"name": "État d'urgence"
},
"model": "memopol_themes.theme",
"pk": 2
},
{
"fields": {
"theme": 1,
...
...
memopol/tests/response_fixtures/ThemeListTest.test_cards.content
0 → 100644
View file @
377706f9
<div class="col-xs-12 col-md-4 theme-card">
<div class="thumbnail">
<a class="custom-thumbnail custom-invisible" href="/theme/acta/">
<div class="row">
<div class="col-xs-12">
<h4 class="text-center">ACTA</h4>
<p class="text-center lead">
<span class="label label-default" data-placement="bottom" data-toggle="tooltip" title="Links">
<span class="glyphicon glyphicon-link"></span>
<span class="badge">1</span>
</span>
<span class="label label-default" data-placement="bottom" data-toggle="tooltip" title="Dossiers">
<span class="glyphicon glyphicon-book"></span>
<span class="badge">1</span>
</span>
<span class="label label-default" data-placement="bottom" data-toggle="tooltip" title="Proposals">
<span class="glyphicon glyphicon-file"></span>
<span class="badge">3</span>
</span>
</p>
</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="/theme/etat-durgence/">
<div class="row">
<div class="col-xs-12">
<h4 class="text-center">État d'urgence</h4>
<p class="text-center lead">
</p>
</div>
</div>
</a>
</div>
</div>
\ No newline at end of file
memopol/tests/response_fixtures/ThemeListTest.test_cards.metadata
0 → 100644
View file @
377706f9
{
"status_code": 200
}
\ No newline at end of file
memopol/tests/response_fixtures/ThemeListTest.test_navbar_order_name_asc.content
0 → 100644
View file @
377706f9
<h4 class="text-center">ACTA</h4>
---
<h4 class="text-center">État d'urgence</h4>
\ No newline at end of file
memopol/tests/response_fixtures/ThemeListTest.test_navbar_order_name_asc.metadata
0 → 100644
View file @
377706f9
{
"status_code": 200
}
\ No newline at end of file
memopol/tests/response_fixtures/ThemeListTest.test_navbar_order_name_desc.content
0 → 100644
View file @
377706f9
<h4 class="text-center">État d'urgence</h4>
---
<h4 class="text-center">ACTA</h4>
\ No newline at end of file
memopol/tests/response_fixtures/ThemeListTest.test_navbar_order_name_desc.metadata
0 → 100644
View file @
377706f9
{
"status_code": 200
}
\ No newline at end of file
memopol/tests/response_fixtures/ThemeListTest.test_navbar_order_options.content
0 → 100644
View file @
377706f9
<li class="disabled">
<a href="?&sort_by=name">name</a>
</li>
---
<li class="disabled">
<a href="?&sort_dir=asc">ascending</a>
</li>
---
<li>
<a href="?&sort_dir=desc">descending</a>
</li>
\ No newline at end of file
memopol/tests/response_fixtures/ThemeListTest.test_navbar_order_options.metadata
0 → 100644
View file @
377706f9
{
"status_code": 200
}
\ No newline at end of file
memopol/tests/test_theme_list.py
0 → 100644
View file @
377706f9
from
.base
import
BaseTest
class
ThemeListTest
(
BaseTest
):
url
=
'/theme/'
def
test_queries
(
self
):
# First query to set session variables
self
.
client
.
get
(
self
.
url
)
with
self
.
assertNumQueries
(
self
.
left_pane_queries
+
3
):
"""
Left pane queries plus:
- 1 for session key
- 1 for theme count (pagination)
- 1 for themes
"""
self
.
client
.
get
(
self
.
url
)
def
test_cards
(
self
):
self
.
selector_test
(
'.theme-card'
)
def
test_navbar_order_options
(
self
):
self
.
selector_test
(
'#listheader #orderby li, #listheader #orderdir li'
)
def
test_navbar_order_name_asc
(
self
):
self
.
selector_test
(
'.theme-card h4'
,
'%s?sort_by=name&sort_dir=asc'
%
self
.
url
)
def
test_navbar_order_name_desc
(
self
):
self
.
selector_test
(
'.theme-card h4'
,
'%s?sort_by=name&sort_dir=desc'
%
self
.
url
)
memopol/views.py
deleted
100644 → 0
View file @
a831f374
# Project specific "glue" coupling of all apps
from
django.db
import
models
from
django.db.models
import
Count
from
django.utils.http
import
urlencode
from
core.views
import
GridListMixin
,
PaginationMixin
,
CSVDownloadMixin
from
representatives
import
views
as
representatives_views
from
representatives.models
import
Representative
from
representatives_votes
import
views
as
votes_views
from
representatives_votes.models
import
Dossier
,
Proposal
from
representatives_positions.forms
import
PositionForm
from
representatives_recommendations.models
import
ScoredVote
class
PaginationFormMixin
(
PaginationMixin
):
"""
Only add an searchparameters to the context to make it easy to paginate
without duplicating the 'page' parameter and keeping the form's GET
parameters.
"""
def
get_context_data
(
self
,
**
kwargs
):
c
=
super
(
PaginationFormMixin
,
self
).
get_context_data
(
**
kwargs
)
params
=
[(
k
,
v
)
for
k
,
v
in
self
.
request
.
GET
.
iteritems
()
if
k
!=
'page'
]
c
[
'searchparameters'
]
=
urlencode
(
dict
(
params
))
return
c
class
RepresentativeList
(
CSVDownloadMixin
,
GridListMixin
,
PaginationFormMixin
,
representatives_views
.
RepresentativeList
):
queryset
=
Representative
.
objects
.
filter
(
active
=
True
).
select_related
(
'score'
)
csv_name
=
'meps.csv'
def
get_csv_results
(
self
,
context
,
**
kwargs
):
qs
=
super
(
RepresentativeList
,
self
).
get_queryset
()
qs
=
qs
.
prefetch_related
(
'email_set'
)
return
[
self
.
add_representative_country_and_main_mandate
(
r
)
for
r
in
qs
]
def
get_csv_row
(
self
,
obj
):
return
(
obj
.
full_name
,
u
', '
.
join
([
e
.
email
for
e
in
obj
.
email_set
.
all
()]),
obj
.
main_mandate
.
group
.
abbreviation
,
obj
.
country
,
)
def
get_context_data
(
self
,
**
kwargs
):
c
=
super
(
RepresentativeList
,
self
).
get_context_data
(
**
kwargs
)
group
=
self
.
kwargs
.
get
(
'group'
,
None
)
group_kind
=
self
.
kwargs
.
get
(
'group_kind'
,
None
)
c
[
'search'
]
=
{
'search'
:
self
.
request
.
GET
.
get
(
'search'
,
None
),
group_kind
:
group
,
}
return
c
class
RepresentativeDetail
(
representatives_views
.
RepresentativeDetail
):
queryset
=
Representative
.
objects
.
select_related
(
'score'
)
def
get_queryset
(
self
):
qs
=
super
(
RepresentativeDetail
,
self
).
get_queryset
()
votes
=
(
ScoredVote
.
objects
.
filter
(
proposal__in
=
Proposal
.
objects
.
exclude
(
recommendation
=
None
))
.
select_related
(
'proposal__recommendation'
)
.
select_related
(
'proposal__dossier'
))
qs
=
qs
.
prefetch_related
(
models
.
Prefetch
(
'votes'
,
queryset
=
votes
))
return
qs
def
get_context_data
(
self
,
**
kwargs
):
c
=
super
(
RepresentativeDetail
,
self
).
get_context_data
(
**
kwargs
)
c
[
'position_form'
]
=
PositionForm
(
initial
=
{
'representative'
:
self
.
object
.
pk
})
self
.
add_representative_country_and_main_mandate
(
c
[
'object'
])
return
c
class
DossierList
(
PaginationFormMixin
,
votes_views
.
DossierList
):
queryset
=
Dossier
.
objects
.
filter
(
proposals__recommendation__isnull
=
False
)
def
get_queryset
(
self
):
qs
=
super
(
DossierList
,
self
).
get_queryset
()
return
qs
.
annotate
(
votes_count
=
Count
(
'proposals__votes'
))
class
DossierDetail
(
votes_views
.
DossierDetail
):
def
get_context_data
(
self
,
**
kwargs
):
c
=
super
(
DossierDetail
,
self
).
get_context_data
(
**
kwargs
)
c
[
'proposals'
]
=
c
[
'dossier'
].
proposals
.
filter
(
recommendation__isnull
=
False
).
select_related
(
'recommendation'
)
# Note: this is a bit of a hack, we feed the RelatedManager with
# the prefetch_related with a clause, so that representative.votes.all
# doesn't query the db but returns what he has in store.
votes
=
(
ScoredVote
.
objects
.
filter
(
proposal__in
=
c
[
'proposals'
])
.
select_related
(
'proposal__recommendation'
))
c
[
'representatives'
]
=
(
Representative
.
objects
.
filter
(
votes__proposal__in
=
c
[
'proposals'
])
.
distinct
()
.
prefetch_related
(
models
.
Prefetch
(
'votes'
,
queryset
=
votes
)))
return
c
memopol/views/theme_list.py
View file @
377706f9
# coding: utf-8
from
core.views
import
PaginationMixin
from
core.views
import
PaginationMixin
,
SortMixin
from
django.db.models
import
Count
from
django.views
import
generic
from
memopol_themes.models
import
Theme
...
...
@@ -9,10 +10,20 @@ from memopol_themes.models import Theme
from
..filters
import
ThemeFilter
class
ThemeList
(
PaginationMixin
,
generic
.
ListView
):
class
ThemeList
(
PaginationMixin
,
SortMixin
,
generic
.
ListView
):
current_filter
=
None
queryset
=
Theme
.
objects
.
all
()
queryset
=
Theme
.
objects
.
all
().
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
)
)
sort_fields
=
{
'name'
:
'name'
,
}
sort_default_field
=
'name'
def
theme_filter
(
self
,
qs
):
f
=
ThemeFilter
(
self
.
request
.
GET
,
queryset
=
qs
)
...
...
@@ -27,4 +38,5 @@ class ThemeList(PaginationMixin, generic.ListView):
def
get_context_data
(
self
,
**
kwargs
):
c
=
super
(
ThemeList
,
self
).
get_context_data
(
**
kwargs
)
c
[
'filter'
]
=
self
.
current_filter
c
[
'view'
]
=
'theme_list'
return
c
static/css/custom.css
View file @
377706f9
...
...
@@ -14,6 +14,13 @@ body {
padding
:
2em
;
}
.pagination
{
margin
:
0
;
}
.card-list
{
margin-top
:
1em
;
}
/***************************************************************
Typographie
...
...
@@ -49,6 +56,10 @@ h3 {
font-weight
:
400
;
}
.label
+
.label
{
margin-left
:
.25em
;
}
a
.custom-invisible
,
a
.custom-invisible
:hover
,
a
.custom-invisible
:focus
{
color
:
#222
;
text-decoration
:
none
;
...
...
templates/memopol_themes/theme_list.html
View file @
377706f9
{% extends "base.html" %}
{% load bootstrap3 %}
{% load i18n %}
{% load
memopol_tags
%}
{% load
humanize
%}
{% block head %}
{{ filter.form.media }}
{% endblock %}
{% block title %}{% trans "Themes" %}{% endblock %}
{% block content %}
<h1
class=
"text-center"
>
{% trans "Themes" %}
</h1>
<p
class=
"lead text-center"
>
{% blocktrans count counter=paginator.count %}{{ counter }} theme{% plural %}{{ counter }} themes{% endblocktrans %}.
</p>
{% include "blocks/listheader.html" %}
<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>
{% block search %}
{% url 'theme-list' as action_url %}
{% include '_filter_form.html' with action=action_url form=filter.form qs=request.GET.urlencode %}
{% endblock %}
<p
class=
"text-center lead"
>
{% if object_list|length == 0 %}
{% if theme.nb_links > 0 %}
<span
class=
"label label-default"
data-toggle=
"tooltip"
data-placement=
"bottom"
title=
"{% trans 'Links' %}"
>
{% bootstrap_icon "link" %}
<span
class=
"badge"
>
{{ theme.nb_links }}
</span>
</span>
{% endif %}
<div
class=
"no-results"
>
No matching themes found :(
{% if theme.nb_dossiers > 0 %}
<span
class=
"label label-default"
data-toggle=
"tooltip"
data-placement=
"bottom"
title=
"{% trans 'Dossiers' %}"
>
{% bootstrap_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' %}"
>
{% bootstrap_icon "file" %}
<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' %}"
>
{% bootstrap_icon "comment" %}
<span
class=
"badge"
>
{{ theme.nb_positions }}
</span>
</span>
{% endif %}
</p>
</div>
</div>
</a>
</div>
</div>
{% endfor %}
</div>
{% include "blocks/listfooter.html" %}
{% else %}
{% include 'core/blocks/pagination.html' %}
<h1>
{% trans "Themes" %}
</h1>
<table>
<tr>
<th>
{% trans "Name" %}
</th>
</tr>
{% for theme in object_list %}
<tr>
<td>
<a
href=
"{% url 'theme-detail' theme.slug %}"
>
{{ theme.name }}
</a>
</td>
</tr>
{% endfor %}
</table>
{% include "core/blocks/pagination.html" %}
{% endif %}
{% endblock %}
templates/representatives/representative_grid.html
View file @
377706f9
...
...
@@ -14,7 +14,7 @@
{% include "blocks/listheader.html" %}
<div
class=
"row"
>
<div
class=
"row
card-list
"
>
{% for representative in object_list %}
<div
class=
"col-xs-12 col-md-4 representative-card"
>
<div
class=
"thumbnail"
>
...
...
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment