define optional sectors via street list for commune (#7603)

This commit is contained in:
Serghei Mihai 2015-06-22 14:46:46 +02:00
parent d0ec829a2b
commit 5bea7ec74e
9 changed files with 384 additions and 38 deletions

View File

@ -16,8 +16,9 @@
from django.utils.text import slugify
from django import forms
from django.utils.translation import ugettext_lazy as _
from .models import EncombrantsManagement
from .models import EncombrantsManagement, Commune, Street
class EncombrantsManagementForm(forms.ModelForm):
class Meta:
@ -33,3 +34,27 @@ class EncombrantsManagementUpdateForm(EncombrantsManagementForm):
class Meta:
model = EncombrantsManagement
exclude = ('users',)
class CommuneForm(forms.ModelForm):
streets = forms.CharField(widget=forms.Textarea(attrs={'cols': 25, 'rows': 10}),
help_text=_('one street by line'),
required=False)
class Meta:
model = Commune
def save(self):
obj = super(CommuneForm, self).save()
if self.cleaned_data['streets']:
streets = self.cleaned_data['streets'].split('\n')
for street in streets:
if street.strip():
Street.objects.create(commune=obj, name=street.strip())
return obj
class StreetsForm(forms.Form):
streets = forms.CharField(widget=forms.Textarea(attrs={'cols': 25, 'rows': 10}),
help_text=_('one street by line'),
required=False)

View File

@ -9,7 +9,7 @@ msgid ""
msgstr ""
"Project-Id-Version: passerelle-montpellier-encombrants 0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-04-30 10:24+0200\n"
"POT-Creation-Date: 2015-07-10 04:15-0500\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: Frederic Peters <fpeters@entrouvet.com>\n"
"Language: French\n"
@ -18,42 +18,53 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
#: models.py:24
#: passerelle_montpellier_encombrants/forms.py:41
#: passerelle_montpellier_encombrants/forms.py:59
msgid "one street by line"
msgstr "une voie par ligne"
#: passerelle_montpellier_encombrants/models.py:24
msgid "Business Process Connectors"
msgstr "Connecteurs métiers"
#: models.py:47
#: passerelle_montpellier_encombrants/models.py:47
msgid "Contact Email"
msgstr "Adresse email de contact"
#: models.py:50 models.py:72
#: passerelle_montpellier_encombrants/models.py:50
#: passerelle_montpellier_encombrants/models.py:64
#: passerelle_montpellier_encombrants/models.py:81
msgid "Sector"
msgstr "Secteur"
#: models.py:61
#: passerelle_montpellier_encombrants/models.py:61
msgid "Name"
msgstr "Nom"
#: models.py:63
#: passerelle_montpellier_encombrants/models.py:63
msgid "INSEE Code"
msgstr "Code INSEE"
#: models.py:73
#: passerelle_montpellier_encombrants/models.py:73
msgid "Street"
msgstr "Voie"
#: passerelle_montpellier_encombrants/models.py:82
msgid "Date"
msgstr "Date"
#: templates/passerelle_montpellier_encombrants/collectday_list.html:5
#: templates/passerelle_montpellier_encombrants/detail.html:32
#: passerelle_montpellier_encombrants/templates/passerelle_montpellier_encombrants/collectday_list.html:5
#: passerelle_montpellier_encombrants/templates/passerelle_montpellier_encombrants/detail.html:32
msgid "Collect Days"
msgstr "Jours de collecte"
#: templates/passerelle_montpellier_encombrants/collectday_list.html:6
#: templates/passerelle_montpellier_encombrants/commune_list.html:6
#: templates/passerelle_montpellier_encombrants/sector_list.html:6
#: passerelle_montpellier_encombrants/templates/passerelle_montpellier_encombrants/collectday_list.html:6
#: passerelle_montpellier_encombrants/templates/passerelle_montpellier_encombrants/commune_list.html:6
#: passerelle_montpellier_encombrants/templates/passerelle_montpellier_encombrants/sector_list.html:6
msgid "New"
msgstr "Nouveau"
#: templates/passerelle_montpellier_encombrants/collectday_list.html:22
#: passerelle_montpellier_encombrants/templates/passerelle_montpellier_encombrants/collectday_list.html:22
msgid ""
"\n"
" There is no collect day yet. Click on the \"New\" button in the top\n"
@ -64,12 +75,27 @@ msgstr ""
"Il n'y a pas encore de jours de collecte définis. Cliquez sur le bouton "
 Nouveau » en haut à droite de la page pour en ajouter un premier."
#: templates/passerelle_montpellier_encombrants/commune_list.html:5
#: templates/passerelle_montpellier_encombrants/detail.html:31
#: passerelle_montpellier_encombrants/templates/passerelle_montpellier_encombrants/commune_detail.html:6
#: passerelle_montpellier_encombrants/templates/passerelle_montpellier_encombrants/commune_list.html:5
#: passerelle_montpellier_encombrants/templates/passerelle_montpellier_encombrants/detail.html:31
msgid "Communes"
msgstr "Communes"
#: templates/passerelle_montpellier_encombrants/commune_list.html:22
#: passerelle_montpellier_encombrants/templates/passerelle_montpellier_encombrants/commune_detail.html:11
msgid "Edit streets"
msgstr "Modifier les voies"
#: passerelle_montpellier_encombrants/templates/passerelle_montpellier_encombrants/commune_detail.html:17
msgid "Limited by the following streets:"
msgstr "Limitée par les voies suivantes :"
msgid ""
"Including all the streets. Click on \"Edit streets\" to add or delete "
"streets."
msgstr ""
"Inclut toutes les voies. Cliquez sur \"Modifes les voies\" pour en ajouter "
"ou supprimer."
msgid ""
"\n"
" There is no commune yet. Click on the \"New\" button in the top\n"
@ -78,38 +104,39 @@ msgid ""
msgstr ""
"\n"
"Il n'y a pas encore de communes définies. Cliquez sur le bouton « Nouveau » "
"en haut à droite de la page pour en ajouter une première."
"en haut à droite de la page pour en ajouter une première.\n"
" "
#: templates/passerelle_montpellier_encombrants/detail.html:7
#: passerelle_montpellier_encombrants/templates/passerelle_montpellier_encombrants/detail.html:7
msgid "edit"
msgstr "modifier"
#: templates/passerelle_montpellier_encombrants/detail.html:10
#: passerelle_montpellier_encombrants/templates/passerelle_montpellier_encombrants/detail.html:10
msgid "delete"
msgstr "supprimer"
#: templates/passerelle_montpellier_encombrants/detail.html:17
#: passerelle_montpellier_encombrants/templates/passerelle_montpellier_encombrants/detail.html:17
msgid "Endpoints"
msgstr "Points d'accès"
#: templates/passerelle_montpellier_encombrants/detail.html:19
#: passerelle_montpellier_encombrants/templates/passerelle_montpellier_encombrants/detail.html:19
msgid "Listing available days:"
msgstr "Lister les jours disponibles :"
#: templates/passerelle_montpellier_encombrants/detail.html:27
#: passerelle_montpellier_encombrants/templates/passerelle_montpellier_encombrants/detail.html:27
msgid "Management"
msgstr "Gestion"
#: templates/passerelle_montpellier_encombrants/detail.html:30
#: templates/passerelle_montpellier_encombrants/sector_list.html:5
#: passerelle_montpellier_encombrants/templates/passerelle_montpellier_encombrants/detail.html:30
#: passerelle_montpellier_encombrants/templates/passerelle_montpellier_encombrants/sector_list.html:5
msgid "Sectors"
msgstr "Secteurs"
#: templates/passerelle_montpellier_encombrants/detail.html:40
#: passerelle_montpellier_encombrants/templates/passerelle_montpellier_encombrants/detail.html:40
msgid "Security"
msgstr "Sécurité"
#: templates/passerelle_montpellier_encombrants/detail.html:43
#: passerelle_montpellier_encombrants/templates/passerelle_montpellier_encombrants/detail.html:43
msgid ""
"Accessing the listings is open, but posting requests is limited to the "
"following API users:"
@ -117,7 +144,7 @@ msgstr ""
"L'accès aux listes est ouvert mais poster des demandes est limité aux "
"utilisateurs suivants :"
#: templates/passerelle_montpellier_encombrants/sector_list.html:23
#: passerelle_montpellier_encombrants/templates/passerelle_montpellier_encombrants/sector_list.html:23
msgid ""
"\n"
" There is no sector yet. Click on the \"New\" button in the top\n"
@ -126,4 +153,5 @@ msgid ""
msgstr ""
"\n"
"Il n'y a pas encore de secteurs définis. Cliquez sur le bouton « Nouveau » "
"en haut à droite de la page pour en ajouter un premier."
"en haut à droite de la page pour en ajouter un premier.\n"
" "

View File

@ -0,0 +1,25 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('passerelle_montpellier_encombrants', '0004_auto_20150430_0326'),
]
operations = [
migrations.CreateModel(
name='Street',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('name', models.CharField(max_length=128, verbose_name='Street')),
('commune', models.ForeignKey(to='passerelle_montpellier_encombrants.Commune')),
],
options={
},
bases=(models.Model,),
),
]

View File

@ -67,6 +67,15 @@ class Commune(models.Model):
return u'%s (%s)' % (self.name, self.insee)
class Street(models.Model):
commune = models.ForeignKey(Commune)
name = models.CharField(max_length=128, blank=False,
verbose_name=_('Street'))
def __unicode__(self):
return '%s, %s' % (self.name, self.commune)
class CollectDay(models.Model):
sector = models.ForeignKey(Sector,
verbose_name=_('Sector'))

View File

@ -0,0 +1,31 @@
{% extends "passerelle_montpellier_encombrants/base.html" %}
{% load i18n %}
{% block more-user-links %}
{{ block.super }}
<a href="{% url 'montpellier-encombrants-commune-listing' %}">{% trans "Communes" %}</a>
{% endblock %}
{% block appbar %}
<h2>{{ commune }}</h2>
<a rel="popup" href="{% url 'montpellier-encombrants-commune-streets-edit-view' pk=commune.pk %}">{% trans 'Edit streets' %}</a>
{% endblock %}
{% block content %}
{% if streets %}
<h3>{% trans "Limited by the following streets:" %}</h3>
<div class="objects-list">
<ul>
{% for street in streets %}
<li>{{ street.name }}</li>
{% endfor %}
</ul>
</div>
{% else %}
<div class="big-msg-info">
{% blocktrans %}Including all the streets. Click on "Edit streets" to add or delete streets.{% endblocktrans %}
</div>
{% endif %}
{% endblock %}

View File

@ -12,8 +12,8 @@
<div class="objects-list">
<ul>
{% for object in object_list %}
<li>{{ object }}
<a rel="popup" class="icon-remove-sign" href="{% url 'montpellier-encombrants-commune-remove' pk=object.id %}"></a></li>
<li><a href="{% url 'montpellier-encombrants-commune-view' pk=object.id %}">{{ object }}</a>
<a rel="popup" class="icon-remove-sign" href="{% url 'montpellier-encombrants-commune-remove' pk=object.id %}"></a> </li>
{% endfor %}
</ul>
</div>

View File

@ -0,0 +1,123 @@
# -*- coding: utf-8 -*-
import json
import datetime
from random import randint
from django.test import TestCase
from django.test.client import Client
from django.core.urlresolvers import reverse
from .models import EncombrantsManagement, Commune, Sector, CollectDay, Street
maurin_streets = ('CHE DES JARDINS DE MAGUELONE', 'RTE DEPARTEMENTALE 132',
'CHE DE SAINT-HUBERT', 'PLAN DU MAS DE SARDAN',
'R DES ROBINIERS')
lattes_streets = ('AV DE BOIRARGUES', 'RPT DE LA FONTVIN', 'PLAN ROSSINI',
'R DES CYCLAMENS', 'RTE DEPARTEMENTALE 172',
'CHE DU MAS DE CAUSSE À L\'ESTELLE')
class EncombrantsTestCase(TestCase):
fixtures = []
def setUp(self):
self.client = Client()
self.instance = EncombrantsManagement.objects.create(title='montpellier',
slug='montpellier',
description='encombrants')
self.now = datetime.datetime.now()
for mail, insee, name in (('cournonsec@montpellier3m.fr', '34087', 'Cournonsec'),
('jacou@montpellier3m.fr', '34120', 'Jacou'),
('castelnau@montpellier3m.fr', '34057', 'Castelnau'),
('prades@montpellier3m.fr', '34217', 'Prades'),
('sussargues@montpellier3m.fr', '34307', 'Sussargues')):
sector = Sector.objects.create(contact_email=mail)
commune = Commune.objects.create(sector=sector, insee=insee, name=name)
for i in xrange(1, randint(1, 10)):
when = self.now + datetime.timedelta(days=i)
CollectDay.objects.create(sector=sector, date=when)
def test_collectdays(self):
for commune in Commune.objects.all():
params = {'adresse': 'PLAN DU MAS DE COCON'}
r = self.client.get(reverse('montpellier-encombrants-available-days',
kwargs={'slug': self.instance.slug, 'insee': commune.insee}))
data = json.loads(r.content)
self.assertNotEqual(data['data'], [])
r = self.client.get(reverse('montpellier-encombrants-available-days',
kwargs={'slug': self.instance.slug,
'insee': commune.insee}), params)
data = json.loads(r.content)
self.assertNotEqual(data['data'], [])
def test_collectdays_in_communes_with_street(self):
maurin_sector = Sector.objects.create(contact_email='maurin@montpellier3m.fr')
maurin_commune = Commune.objects.create(sector=maurin_sector, name='Maurin',
insee='34970')
lattes_sector = Sector.objects.create(contact_email='lattes@montpellier3m.fr')
lattes_commune = Commune.objects.create(sector=lattes_sector, name='Lattes',
insee='34970')
for s in maurin_streets:
Street.objects.create(commune=maurin_commune, name=s)
CollectDay.objects.create(sector=maurin_sector,
date=self.now + datetime.timedelta(days=2))
# Maurin
r = self.client.get(reverse('montpellier-encombrants-available-days',
kwargs={'slug': self.instance.slug,
'insee': '34970'}), {'adresse': 'PLAN DU MAS DE SARDAN'})
data = json.loads(r.content)
self.assertNotEqual(data['data'], [])
# Lattes
for s in lattes_streets:
Street.objects.create(commune=lattes_commune, name=s)
r = self.client.get(reverse('montpellier-encombrants-available-days',
kwargs={'slug': self.instance.slug,
'insee': '34970'}), {'adresse': 'ROUTE DEPARTEMENTALE 172'})
data = json.loads(r.content)
self.assertEqual(data['data'], [])
CollectDay.objects.create(sector=lattes_sector,
date=self.now + datetime.timedelta(days=4))
r = self.client.get(reverse('montpellier-encombrants-available-days',
kwargs={'slug': self.instance.slug,
'insee': '34970'}), {'adresse': 'ROUTE DEPARTEMENTALE 172'})
data = json.loads(r.content)
self.assertNotEqual(data['data'], [])
def test_collectdays_nonexisting_street(self):
r = self.client.get(reverse('montpellier-encombrants-available-days',
kwargs={'slug': self.instance.slug,
'insee': '34970'}), {'adresse': 'Nonexisting street'})
data = json.loads(r.content)
self.assertNotEqual(data['data'], [])
def test_collectdays_random_case_street(self):
lattes_sector = Sector.objects.create(contact_email='lattes@montpellier3m.fr')
lattes_commune = Commune.objects.create(sector=lattes_sector, name='Lattes',
insee='34970')
for s in lattes_streets:
Street.objects.create(commune=lattes_commune, name=s)
CollectDay.objects.create(sector=lattes_sector,
date=self.now + datetime.timedelta(days=10))
r = self.client.get(reverse('montpellier-encombrants-available-days',
kwargs={'slug': self.instance.slug,
'insee': '34970'}), {'adresse': 'route departementale 172'})
data = json.loads(r.content)
self.assertNotEqual(data['data'], [])

View File

@ -46,6 +46,10 @@ management_urlpatterns = patterns('',
name='montpellier-encombrants-commune-listing'),
url(r'^communes/add$', CommuneCreateView.as_view(),
name='montpellier-encombrants-commune-add'),
url(r'^communes/(?P<pk>[\w,-]+)$', CommuneView.as_view(),
name='montpellier-encombrants-commune-view'),
url(r'^communes/(?P<pk>[\w,-]+)/streets$', StreetEditView.as_view(),
name='montpellier-encombrants-commune-streets-edit-view'),
url(r'^communes/(?P<pk>[\w,-]+)/delete$', CommuneDeleteView.as_view(),
name='montpellier-encombrants-commune-remove'),
url(r'^collectdays/$', CollectDayListView.as_view(),

View File

@ -20,12 +20,58 @@ from django.core.urlresolvers import reverse, reverse_lazy
from django.views.generic import DetailView, ListView
from django.views.generic.base import View
from django.views.generic.detail import SingleObjectMixin
from django.views.generic.edit import CreateView, UpdateView, DeleteView
from django.views.generic.edit import CreateView, UpdateView, DeleteView, FormView
from django.db import transaction
from passerelle import utils
from .models import EncombrantsManagement, Sector, Commune, CollectDay
from .forms import EncombrantsManagementForm, EncombrantsManagementUpdateForm
from .models import EncombrantsManagement, Sector, Commune, CollectDay, Street
from .forms import EncombrantsManagementForm, EncombrantsManagementUpdateForm, CommuneForm, StreetsForm
prefix_map = {
'ALL': 'ALLEE',
'AUTO': 'AUTOROUTE',
'AV': 'AVENUE',
'BASS': 'BASSIN',
'BD': 'BOULEVARD',
'CAR': 'CARREFOUR',
'CHE': 'CHAUSSEE',
'COUR': 'COUR',
'CRS': 'COURS',
'DESC': 'DESCENTE',
'DOM': 'DOMAINE',
'ENCL': 'ENCLOS',
'ESP': 'ESPLANADE',
'ESPA': 'ESPACE',
'GR': '', # "GR GRAND-RUE JEAN MOULIN"
'IMP': 'IMPASSE',
'JARD': 'JARDIN',
'MAIL': '', # "MAIL LE GRAND MAIL"
'PARC': 'PARC',
'PARV': '', # "PARV PARVIS DE LA LEGION D HONNEUR"
'PAS': 'PASSAGE',
'PL': 'PLACE',
'PLAN': 'PLAN',
'PONT': 'PONT',
'QUA': 'QUAI',
'R': 'RUE',
'RAMB': '', # "RAMB RAMBLA DES CALISSONS"
'RPT': 'ROND-POINT',
'RTE': 'ROUTE',
'SQ': 'SQUARE',
'TSSE': '', # "TSSE TERRASSE DES ALLEES DU BOIS"
'TUN': 'TUNNEL',
'VIAD': 'VIADUC',
'VOI': 'VOIE',
}
def prefix_cleanup(name):
name = name.strip()
for prefix, full in prefix_map.items():
if name.startswith(prefix + ' '):
name = (full + name[len(prefix):]).strip()
return name
class EncombrantsManagementDetailView(DetailView):
model = EncombrantsManagement
@ -90,10 +136,22 @@ class CommuneListView(ListView):
class CommuneCreateView(CreateView):
model = Commune
form_class = CommuneForm
template_name = 'passerelle/manage/service_form.html'
success_url = reverse_lazy('montpellier-encombrants-commune-listing')
class CommuneView(DetailView):
model = Commune
def get_context_data(self, **kwargs):
context = super(CommuneView, self).get_context_data(**kwargs)
context['object'] = EncombrantsManagement.objects.all()[0]
context['commune'] = self.object
context['streets'] = self.object.street_set.all()
return context
class CommuneDeleteView(DeleteView):
model = Commune
template_name = 'passerelle/manage/service_confirm_delete.html'
@ -125,12 +183,55 @@ class AvailableDaysView(View, SingleObjectMixin):
model = EncombrantsManagement
def get(self, request, *args, **kwargs):
days = CollectDay.objects.filter(
date__gt=datetime.datetime.today()+datetime.timedelta(days=1),
sector__commune__insee=kwargs.get('insee')).order_by('date')
address = request.GET.get('adresse')
collect_days = []
communes = Commune.objects.filter(insee=kwargs.get('insee'))
if not communes:
communes = Commune.objects.all()
for commune in communes:
days = commune.sector.collectday_set.filter(date__gt=datetime.datetime.today()+datetime.timedelta(days=1)).order_by('date')
if address and commune.street_set.all():
for s in commune.street_set.all():
if prefix_cleanup(s.name) in address.upper():
collect_days.extend(days)
break
else:
collect_days.extend(days)
limit = request.GET.get('limit')
result = [{'id': x.date.strftime('%Y-%m-%d'),
'text': x.date.strftime('%d/%m/%Y')} for x in days]
'text': x.date.strftime('%d/%m/%Y')} for x in collect_days]
if limit:
result = result[:int(limit)]
return utils.response_for_json(request, {'data': result})
class StreetEditView(FormView):
form_class = StreetsForm
template_name = 'passerelle/manage/service_form.html'
def get_success_url(self):
return reverse('montpellier-encombrants-commune-view',
kwargs={'pk': self.kwargs['pk']})
def get_initial(self):
try:
c = Commune.objects.get(pk=self.kwargs['pk'])
streets = c.street_set.all()
if streets:
return {'streets': '\n'.join([s.name for s in streets])}
except Commune.DoesNotExist:
pass
def form_valid(self, form):
try:
c = Commune.objects.get(pk=self.kwargs['pk'])
with transaction.atomic():
c.street_set.all().delete()
for s in form.cleaned_data.get('streets', '').split('\n'):
s = s.strip()
if s:
Street.objects.create(commune=c, name=s)
except Commune.DoesNotExist:
pass
return super(StreetEditView, self).form_valid(form)