Commit 0731c495 authored by Nicolas Joyard's avatar Nicolas Joyard
Browse files

Add working position form

parent f6d6bd9a
...@@ -213,7 +213,7 @@ TEMPLATE_LOADERS = ( ...@@ -213,7 +213,7 @@ TEMPLATE_LOADERS = (
TEMPLATE_CONTEXT_PROCESSORS = global_settings.TEMPLATE_CONTEXT_PROCESSORS + ( TEMPLATE_CONTEXT_PROCESSORS = global_settings.TEMPLATE_CONTEXT_PROCESSORS + (
'django.template.context_processors.request', 'django.template.context_processors.request',
'memopol.context_processors.search_form_options' 'memopol.context_processors.search_form_options',
) )
# Static files finders # Static files finders
......
...@@ -7,13 +7,17 @@ class BaseTest(ResponseDiffTestMixin, test.TestCase): ...@@ -7,13 +7,17 @@ class BaseTest(ResponseDiffTestMixin, test.TestCase):
""" """
Common queries Common queries
- 1 for chambers - 5 for search forms
- 1 for countries - 1 for chambers
- 1 for parties - 1 for countries
- 1 for committees - 1 for parties
- 1 for delegations - 1 for committees
- 1 for delegations
- 2 for the position form
- 1 for representatives
- 1 for themes
""" """
left_pane_queries = 5 left_pane_queries = 7
def request_test(self, url=None): def request_test(self, url=None):
self.assertResponseDiffEmpty(self.client.get(url or self.url)) self.assertResponseDiffEmpty(self.client.get(url or self.url))
......
<form action="" method="post">
<div class="modal-header">
<button aria-label="Close" class="close" data-dismiss="modal" type="button"><span aria-hidden="true">×</span></button>
<h4 class="modal-title">Add a representative public position</h4>
</div>
<div class="modal-body">
<input name="csrfmiddlewaretoken" type="hidden" value="csrftoken"/>
<div class="row">
<div class="col-sm-12">
<div class="well well-sm text-justify">
<p>
Use this form to submit a public position taken by a representative and
related to one of the themes followed on this instance of Political Memory.
Public positions may include blog or social network posts, interviews,
parliament interventions...
</p>
<p>
Be sure to include a relevant excerpt from the public position as well as
a valid link that refers to it. Note that positions will be reviewed by
the staff before publication.
</p>
</div>
</div>
</div>
<div class="row">
<div class="col-sm-6">
<div class="form-group"><label class="col-md-3 control-label" for="id_position-representative">Representative</label><div class="col-md-9"><select class="form-control" id="id_position-representative" name="position-representative" required="required" title="">
<option selected="selected" value="">---------</option>
<option value="4902">François Asensi</option>
<option value="4893">Thierry Benoit</option>
<option value="4898">Marcel Bonnot</option>
<option value="4891">Jean-Claude Bouchet</option>
<option value="22">Paul BRANNEN</option>
<option value="12">Udo BULLMANN</option>
<option value="4914">Jean-Paul Chanteguet</option>
<option value="4912">Jean-Louis Christ</option>
<option value="4900">Jean-Michel Couve</option>
<option value="21">Esther de LANGE</option>
<option value="9">Albert DESS</option>
<option value="4896">Marc Dolez</option>
<option value="4889">Dominique Dord</option>
<option value="4899">Olivier Dussopt</option>
<option value="4890">Daniel Fasquelle</option>
<option value="24">María Teresa GIMÉNEZ BARBAT</option>
<option value="4894">Claude Goasguen</option>
<option value="4885">Pascale Got</option>
<option value="13">Bolesław G. PIECHA</option>
<option value="20">Iveta GRIGULE</option>
<option value="1">Czesław HOC</option>
<option value="4910">Philippe Houillon</option>
<option value="7">Sylvia-Yvonne KAUFMANN</option>
<option value="18">Jan KELLER</option>
<option value="3">Dietmar KÖSTER</option>
<option value="29">Werner LANGEN</option>
<option value="23">Jo LEINEN</option>
<option value="4903">Pierre Lellouche</option>
<option value="4886">Annick Lepetit</option>
<option value="4895">Pierre Lequiller</option>
<option value="8">Arne LIETZ</option>
<option value="19">Verónica LOPE FONTAGNÉ</option>
<option value="4908">Jacqueline Maquet</option>
<option value="4907">Philippe Martin</option>
<option value="25">Gesine MEISSNER</option>
<option value="4904">Hervé Morin</option>
<option value="4913">Alain Moyne-Bressand</option>
<option value="16">Angelika NIEBLER</option>
<option value="14">Paul NUTTALL</option>
<option value="5">Patrick O'FLYNN</option>
<option value="4905">Martine Pinville</option>
<option value="15">Mirosław PIOTROWSKI</option>
<option value="4906">François Pupponi</option>
<option value="4911">Jean-Luc Reitzer</option>
<option value="4909">Franck Reynier</option>
<option value="4888">Marcel Rogemont</option>
<option value="4901">André Santini</option>
<option value="10">Annie SCHREIJER-PIERIK</option>
<option value="28">Joachim SCHUSTER</option>
<option value="26">Helga STEVENS</option>
<option value="17">László TŐKÉS</option>
<option value="4887">Jean-Jacques Urvoas</option>
<option value="4892">Alain Vidalies</option>
<option value="4897">Philippe Vigier</option>
<option value="6">Axel VOSS</option>
<option value="2">Renate WEBER</option>
<option value="11">Kerstin WESTPHAL</option>
<option value="4">Hermann WINKLER</option>
<option value="27">Damiano ZOFFOLI</option>
</select></div></div>
<div class="form-group"><label class="col-md-3 control-label" for="id_position-datetime">Datetime</label><div class="col-md-9">
<div class="input-group date" id="id_position-datetime">
<input class="form-control" id="id_position-datetime" name="position-datetime" placeholder="Datetime" readonly="" required="required" title="" type="text"/>
<span class="input-group-addon"><span class="glyphicon glyphicon-remove"></span></span>
<span class="input-group-addon"><span class="glyphicon glyphicon-calendar"></span></span>
</div>
<script type="text/javascript">
$("#id_position-datetime").datetimepicker({minView: 2,
autoclose: true,
language: 'en',
startView: 2,
format: 'yyyy-mm-dd'}).find('input').addClass("form-control");
</script>
</div></div>
<div class="form-group"><label class="col-md-3 control-label" for="id_position-link">Link</label><div class="col-md-9"><input class="form-control" id="id_position-link" maxlength="500" name="position-link" placeholder="Link" required="required" title="" type="url"/></div></div>
</div>
<div class="col-sm-6">
<div class="form-group"><label class="col-md-3 control-label" for="id_position-themes_0">Themes</label><div class="col-md-9"><div id="id_position-themes"><div class="checkbox"><label for="id_position-themes_0"><input class="" id="id_position-themes_0" name="position-themes" title="" type="checkbox" value="1"/> Etat d'urgence</label></div>
<div class="checkbox"><label for="id_position-themes_1"><input class="" id="id_position-themes_1" name="position-themes" title="" type="checkbox" value="2"/> ACTA</label></div></div></div></div>
</div>
</div>
<div class="row">
<div class="col-sm-12">
<div class="col-sm-12">
<div class="form-group"><label class="control-label" for="id_position-text">Text</label><textarea class="form-control" cols="40" id="id_position-text" name="position-text" placeholder="Text" required="required" rows="10" title=""></textarea></div>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-default" data-dismiss="modal" type="button">Close</button>
<button class="btn btn-primary" type="submit">Submit public position</button>
</div>
</form>
\ No newline at end of file
<option selected="selected" value="4899">Olivier Dussopt</option>
\ No newline at end of file
<input checked="checked" class="" id="id_position-themes_0" name="position-themes" title="" type="checkbox" value="1"/>
\ No newline at end of file
from .base import BaseTest, RepresentativeBaseTest, ThemeBaseTest
class PositionFormTest(BaseTest):
url = '/'
def test_position_form(self):
self.client.cookies['csrftoken'] = 'csrftoken'
self.selector_test('#add-position-form form')
def test_select_representative(self):
self.selector_test(
'#add-position-form #id_position-representative option[selected]',
RepresentativeBaseTest.base_url % 'none'
)
def test_select_theme(self):
self.selector_test(
'#add-position-form #id_position-themes input[checked]',
ThemeBaseTest.base_url % 'none'
)
# coding: utf-8 # coding: utf-8
from django.conf.urls import include, url from django.conf.urls import include, url
from django.contrib import admin from django.contrib import admin
from django.views import generic
from views.home import HomeView
from views.dossier_ac import DossierAutocomplete, ProposalAutocomplete from views.dossier_ac import DossierAutocomplete, ProposalAutocomplete
from views.dossier_detail_base import DossierDetailBase from views.dossier_detail_base import DossierDetailBase
...@@ -184,8 +185,6 @@ urlpatterns = [ ...@@ -184,8 +185,6 @@ urlpatterns = [
), ),
url(r'^admin/', include(admin.site.urls)), url(r'^admin/', include(admin.site.urls)),
url(r'^positions/', include('representatives_positions.urls',
namespace='representatives_positions')),
url(r'^api/', include(api.router.urls)), url(r'^api/', include(api.router.urls)),
url(r'^$', generic.TemplateView.as_view(template_name='home.html')), url(r'^$', HomeView.as_view()),
] ]
...@@ -4,8 +4,10 @@ from django.views import generic ...@@ -4,8 +4,10 @@ from django.views import generic
from representatives_votes.models import Dossier from representatives_votes.models import Dossier
from representatives_positions.views import PositionFormMixin
class DossierDetailBase(generic.DetailView):
class DossierDetailBase(PositionFormMixin, generic.DetailView):
template_name = 'representatives_votes/dossier_detail.html' template_name = 'representatives_votes/dossier_detail.html'
queryset = Dossier.objects.prefetch_related('themes') queryset = Dossier.objects.prefetch_related('themes')
...@@ -9,8 +9,11 @@ from representatives_votes.models import Dossier ...@@ -9,8 +9,11 @@ from representatives_votes.models import Dossier
from ..filters import DossierFilter from ..filters import DossierFilter
from representatives_positions.views import PositionFormMixin
class DossierList(PaginationMixin, SortMixin, generic.ListView):
class DossierList(PaginationMixin, SortMixin, PositionFormMixin,
generic.ListView):
current_filter = None current_filter = None
queryset = Dossier.objects.prefetch_related( queryset = Dossier.objects.prefetch_related(
......
# coding: utf-8
from django.views import generic
from representatives_positions.views import PositionFormMixin
class HomeView(PositionFormMixin, generic.TemplateView):
template_name = 'home.html'
...@@ -8,8 +8,11 @@ from representatives.models import Chamber, Representative, Address, Phone, \ ...@@ -8,8 +8,11 @@ from representatives.models import Chamber, Representative, Address, Phone, \
from .representative_mixin import RepresentativeViewMixin from .representative_mixin import RepresentativeViewMixin
from representatives_positions.views import PositionFormMixin
class RepresentativeDetailBase(RepresentativeViewMixin, generic.DetailView):
class RepresentativeDetailBase(RepresentativeViewMixin, PositionFormMixin,
generic.DetailView):
queryset = Representative.objects.select_related('score') queryset = Representative.objects.select_related('score')
...@@ -56,5 +59,6 @@ class RepresentativeDetailBase(RepresentativeViewMixin, generic.DetailView): ...@@ -56,5 +59,6 @@ class RepresentativeDetailBase(RepresentativeViewMixin, generic.DetailView):
c = super(RepresentativeDetailBase, self).get_context_data(**kwargs) c = super(RepresentativeDetailBase, self).get_context_data(**kwargs)
self.add_representative_country_and_main_mandate(c['object']) self.add_representative_country_and_main_mandate(c['object'])
c['position_form'].fields['representative'].initial = c['object'].pk
return c return c
...@@ -10,10 +10,12 @@ from representatives.models import Representative ...@@ -10,10 +10,12 @@ from representatives.models import Representative
from ..filters import RepresentativeFilter from ..filters import RepresentativeFilter
from .representative_mixin import RepresentativeViewMixin from .representative_mixin import RepresentativeViewMixin
from representatives_positions.views import PositionFormMixin
class RepresentativeList(CSVDownloadMixin, GridListMixin, PaginationMixin, class RepresentativeList(CSVDownloadMixin, GridListMixin, PaginationMixin,
RepresentativeViewMixin, ActiveLegislatureMixin, RepresentativeViewMixin, ActiveLegislatureMixin,
SortMixin, generic.ListView): SortMixin, PositionFormMixin, generic.ListView):
csv_name = 'representatives' csv_name = 'representatives'
queryset = Representative.objects.select_related('score') queryset = Representative.objects.select_related('score')
......
...@@ -4,8 +4,16 @@ from django.views import generic ...@@ -4,8 +4,16 @@ from django.views import generic
from memopol_themes.models import Theme from memopol_themes.models import Theme
from representatives_positions.views import PositionFormMixin
class ThemeDetailBase(generic.DetailView):
class ThemeDetailBase(PositionFormMixin, generic.DetailView):
template_name = 'memopol_themes/theme_detail.html' template_name = 'memopol_themes/theme_detail.html'
queryset = Theme.objects.all() queryset = Theme.objects.all()
def get_context_data(self, **kwargs):
c = super(ThemeDetailBase, self).get_context_data(**kwargs)
c['position_form'].fields['themes'].initial = [c['object']]
return c
...@@ -9,8 +9,11 @@ from memopol_themes.models import Theme ...@@ -9,8 +9,11 @@ from memopol_themes.models import Theme
from ..filters import ThemeFilter from ..filters import ThemeFilter
from representatives_positions.views import PositionFormMixin
class ThemeList(PaginationMixin, SortMixin, generic.ListView):
class ThemeList(PaginationMixin, SortMixin, PositionFormMixin,
generic.ListView):
current_filter = None current_filter = None
queryset = Theme.objects.all().annotate( queryset = Theme.objects.all().annotate(
......
...@@ -2,21 +2,36 @@ from django import forms ...@@ -2,21 +2,36 @@ from django import forms
from datetimewidget.widgets import DateWidget from datetimewidget.widgets import DateWidget
from memopol_themes.models import Theme
from .models import Position from .models import Position
class PositionForm(forms.ModelForm): class PositionForm(forms.ModelForm):
themes = forms.models.ModelMultipleChoiceField(
queryset=Theme.objects.all(),
required=False,
widget=forms.CheckboxSelectMultiple
)
class Meta: class Meta:
model = Position model = Position
fields = ['datetime', 'text', 'link', 'representative'] fields = ['representative', 'link', 'datetime', 'themes', 'text']
widgets = { widgets = {
# Use localization and bootstrap 3
'datetime': DateWidget( 'datetime': DateWidget(
attrs={
'id': 'yourdatetimeid'
},
usel10n=True, usel10n=True,
bootstrap_version=3, bootstrap_version=3
), )
'representative': forms.HiddenInput
} }
def save(self, commit=True):
position = super(PositionForm, self).save(commit=False)
if commit:
position.save()
if position.pk:
position.themes = self.cleaned_data.get('themes')
self.save_m2m()
return position
from django.db import models from django.db import models
from django.core.urlresolvers import reverse
from django.template.defaultfilters import truncatewords from django.template.defaultfilters import truncatewords
from representatives.models import Representative from representatives.models import Representative
...@@ -21,7 +20,3 @@ class Position(models.Model): ...@@ -21,7 +20,3 @@ class Position(models.Model):
def unpublish(self): def unpublish(self):
self.published = False self.published = False
def get_absolute_url(self):
return reverse('representatives_positions:position-detail',
args=(self.pk,))
from django.conf.urls import url
import views
urlpatterns = [
url(
r'^position/create/$',
views.PositionCreate.as_view(),
name='position-create'
),
url(
r'^position/(?P<pk>\d+)/$',
views.PositionDetail.as_view(),
name='position-detail'
),
]
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment