427 lines
17 KiB
Python
427 lines
17 KiB
Python
# vim:spell:spelllang=fr
|
|
# -*- encoding: utf-8 -*-
|
|
|
|
import datetime
|
|
|
|
from django.conf.urls.defaults import patterns, url
|
|
from django.utils.translation import ugettext_lazy as _
|
|
from django.contrib.admin import AdminSite, ModelAdmin
|
|
from django.contrib.gis.db.models import GeometryField
|
|
from django.contrib.gis import admin
|
|
from django.contrib.auth.admin import UserAdmin, GroupAdmin
|
|
from django.contrib.auth.models import Group, User
|
|
from django.contrib.admin.util import flatten_fieldsets
|
|
from django.db.models import ManyToManyField
|
|
from django.forms import CheckboxSelectMultiple
|
|
from django.http import HttpResponseRedirect
|
|
from django.contrib.admin.util import unquote
|
|
from django.contrib import messages
|
|
from django.conf import settings
|
|
from django.contrib import admin as contrib_admin
|
|
from django.contrib.admin.widgets import FilteredSelectMultiple
|
|
import django.contrib.auth.forms
|
|
from django.contrib.admin import RelatedFieldListFilter
|
|
from django.contrib.admin import SimpleListFilter
|
|
|
|
|
|
from daterange_filter.filter import DateRangeFilter
|
|
|
|
|
|
import models
|
|
import admin_views
|
|
from forms import ProfilRechercheCreationForm, InvalidationForm, \
|
|
ValidationForm
|
|
import utils
|
|
import actions
|
|
|
|
class AdministrateurGroupListFilter(RelatedFieldListFilter):
|
|
def __init__(self, *args, **kwargs):
|
|
super(AdministrateurGroupListFilter, self).__init__(*args, **kwargs)
|
|
self.lookup_choices = [
|
|
(group.id, group.name) for group in Group.objects \
|
|
.filter(user__is_staff=True).distinct()
|
|
]
|
|
|
|
|
|
class LastLoginFilter(SimpleListFilter):
|
|
title = _(u'Dernière connection')
|
|
|
|
parameter_name = 'last-login-period'
|
|
|
|
def lookups(self, request, model_admin):
|
|
return (
|
|
('2y', _(u'Il y a plus de deux ans')),
|
|
('1y', _(u'Il y a plus d\'un ans')),
|
|
('6m', _(u'Il y a plus de 6 mois')),
|
|
)
|
|
|
|
def queryset(self, request, queryset):
|
|
days = {
|
|
'2y': 365*2,
|
|
'1y': 365,
|
|
'6m': 183
|
|
}
|
|
if self.value() in days:
|
|
not_after = datetime.date.today() - datetime.timedelta(days=days[self.value()])
|
|
queryset = queryset.filter(last_login__lte=not_after)
|
|
return queryset
|
|
|
|
|
|
class OverloadAddForm(object):
|
|
def get_fieldsets(self, request, obj=None):
|
|
'''
|
|
Utilise un fieldsets spécifique pour la création, sinon ça bug avec le
|
|
formulaire modifié.
|
|
'''
|
|
if not obj:
|
|
return self.add_fieldsets
|
|
return super(OverloadAddForm, self).get_fieldsets(request, obj)
|
|
|
|
def get_form(self, request, obj=None, **kwargs):
|
|
"""
|
|
Utilise un formulaire spécifique pour la création (idée emprunté à
|
|
django.contrib.auth.admin)
|
|
"""
|
|
defaults = {}
|
|
if obj is None:
|
|
defaults.update({
|
|
'form': self.add_form,
|
|
'fields': flatten_fieldsets(self.add_fieldsets),
|
|
})
|
|
defaults.update(kwargs)
|
|
return super(OverloadAddForm, self).get_form(request, obj, **defaults)
|
|
|
|
class ProfilRechercheAdmin(OverloadAddForm, ModelAdmin):
|
|
list_display = ('email', 'cas', 'types_d_offre', 'is_active')
|
|
list_filter = ('is_active', LastLoginFilter, 'type_d_offre','acceptation_cgu','acceptation_charte_qualite')
|
|
readonly_fields = ('email', 'cas', 'acceptation_cgu',
|
|
'acceptation_charte_qualite', 'last_login',)
|
|
fieldsets = (
|
|
(_(u'Identifiants'), { 'fields': (('email', 'cas'),) }),
|
|
(_(u'Attributs'), { 'fields': ('is_active', 'type_d_offre') }),
|
|
(_(u"Conditions d'utilisation"), { 'fields': (('acceptation_cgu',
|
|
'acceptation_charte_qualite'),) }))
|
|
search_fields = ( 'email', 'user_ptr__utilisateur_cas__identifiant' )
|
|
ordering = ('email',)
|
|
add_fieldsets = (
|
|
(None, {
|
|
'classes': ('wide', ),
|
|
'fields': ('identifiant_cas', )
|
|
}),
|
|
)
|
|
add_form = ProfilRechercheCreationForm
|
|
formfield_overrides = {
|
|
ManyToManyField: { 'widget': CheckboxSelectMultiple },
|
|
}
|
|
actions = [ actions.export_as_csv, actions.desactivate_user, actions.activate_user ]
|
|
|
|
def types_d_offre(self, instance):
|
|
return ''.join(map(lambda s: '%s<br>' % s, instance.type_d_offre.values_list('nom', flat=True)))
|
|
types_d_offre.allow_tags = True
|
|
types_d_offre.short_description = _("Types d'offre")
|
|
|
|
def cas(self, instance):
|
|
'''
|
|
|
|
Champ virtuel pour le lien à un utilisateur CAS
|
|
'''
|
|
q = instance.user_ptr.utilisateur_cas.all()
|
|
return q[0].identifiant if q else ''
|
|
cas.short_description = _(u'Identifiant CAS')
|
|
|
|
def get_urls(self):
|
|
urls = super(ProfilRechercheAdmin, self).get_urls()
|
|
my_urls = patterns('',
|
|
url(r'^injection/$', admin_views.VueInjection.as_view(), { 'media': self.media }),)
|
|
return my_urls + urls
|
|
|
|
def save_model(self, request, obj, form, change):
|
|
res = super(ProfilRechercheAdmin, self).save_model(request, obj, form, change)
|
|
if change:
|
|
if obj.is_active:
|
|
request.logger.info(u"active le profil recherche %s", obj)
|
|
else:
|
|
request.logger.info(u"désactive le profil recherche %s", obj);
|
|
else:
|
|
request.record('nouveau-compte-recherche-manuel',
|
|
'nouveau compte CAS {cas_user}',
|
|
cas_user=form.cleaned_data['identifiant_cas'],
|
|
new_user=obj)
|
|
return res
|
|
|
|
def queryset(self, request):
|
|
'''
|
|
Précharge l'object parent ainsi que les éventuels liens avec
|
|
des utilisateurs CAS pour optimiser le nombre de requêtes.
|
|
'''
|
|
qs = super(ProfilRechercheAdmin, self).queryset(request)
|
|
qs = qs.prefetch_related('user_ptr', 'user_ptr__utilisateur_cas')
|
|
return qs
|
|
|
|
class ProfilOffreAdmin(ModelAdmin):
|
|
list_display = ('email', 'is_active', 'accepte_notif_alertes', 'accepte_notif_depublication', 'accepte_invitations')
|
|
list_filter = (
|
|
LastLoginFilter,
|
|
('date_joined', DateRangeFilter),
|
|
'is_active',
|
|
'sans_validation',
|
|
'accepte_notif_alertes',
|
|
'accepte_notif_depublication',
|
|
'accepte_invitations',
|
|
'acceptation_cgu',
|
|
'acceptation_charte_qualite')
|
|
search_fields = list_display
|
|
ordering = list_display
|
|
readonly_fields = ('email', 'acceptation_cgu', 'acceptation_charte_qualite',
|
|
'accepte_notif_alertes', 'accepte_notif_depublication', 'accepte_invitations',
|
|
'comment', 'last_login')
|
|
fieldsets = (
|
|
(_(u'Identifiants'), { 'fields': ('email','url_de_contact') }),
|
|
(_(u'Attributs'), { 'fields': ('is_active', 'sans_validation') }),
|
|
(_(u"Conditions d'utilisation"), { 'fields': (('acceptation_cgu',
|
|
'acceptation_charte_qualite'),) }),
|
|
(_(u'Notifications par mail'), { 'fields':
|
|
(('accepte_notif_alertes', 'accepte_notif_depublication', 'accepte_invitations'),) }),
|
|
(_(u'Informations complémentaires'), { 'fields':
|
|
('first_name', 'last_name', 'address', 'phone', 'mobile', 'job', 'comment', 'last_login')}))
|
|
actions = [ actions.export_as_csv, actions.desactivate_user, actions.activate_user ]
|
|
|
|
def save_model(self, request, obj, form, change):
|
|
if change:
|
|
if obj.is_active:
|
|
request.logger.info(u"active le profil offre %s", obj)
|
|
else:
|
|
request.logger.info(u"désactive le profil offre %s", obj);
|
|
else:
|
|
request.logger.info(u"crée le profil offre %s", obj)
|
|
return super(ProfilOffreAdmin, self).save_model(request, obj, form, change)
|
|
|
|
class MyOSMGeoAdmin(admin.OSMGeoAdmin):
|
|
default_zoom = 12
|
|
default_lat = 6251249.4833769
|
|
default_lon = 261223.54412843
|
|
|
|
class ZoneAdmin(admin.OSMGeoAdmin):
|
|
default_zoom = 10
|
|
def formfield_for_dbfield(self, db_field, **kwargs):
|
|
"""
|
|
Overloaded from ModelAdmin so that an OpenLayersWidget is used
|
|
for viewing/editing GeometryField.
|
|
"""
|
|
if isinstance(db_field, GeometryField):
|
|
request = kwargs.pop('request', None)
|
|
# Setting the widget with the newly defined widget.
|
|
kwargs['widget'] = self.get_map_widget(db_field, request=request)
|
|
return db_field.formfield(**kwargs)
|
|
else:
|
|
return super(ZoneAdmin, self).formfield_for_dbfield(db_field, **kwargs)
|
|
|
|
def get_map_widget(self, db_field, request=None):
|
|
try:
|
|
id = int(filter(None, request.path.split('/'))[-1])
|
|
except ValueError:
|
|
id = None
|
|
qs = self.queryset(request).exclude(id=id)
|
|
more_wkt = [ (zone.nom, zone.polygon.transform(900913, clone=True).wkt) for zone in qs]
|
|
cls = super(ZoneAdmin, self).get_map_widget(db_field)
|
|
cls.params['more_wkt'] = more_wkt
|
|
return cls
|
|
|
|
class AnnonceAdmin(MyOSMGeoAdmin):
|
|
fieldsets = [
|
|
(_('Validation'), { 'fields': ['etat_validation',
|
|
'derniere_publication', 'derniere_validation',
|
|
'derniere_validation_par', 'raison_invalidation',
|
|
'nos_logements' ] } ),
|
|
(_("Champs de l'annonce"), { 'fields': [ 'type_de_logement',
|
|
'proprietaire', 'prix_par_mois', 'duree_location', 'monnaie', 'surface_en_m2',
|
|
'pays', 'ville', 'position_geographique', 'type_d_offre',
|
|
'prestations', 'photo', 'description' ] })]
|
|
readonly_fields = [ 'etat_validation', 'derniere_publication', 'derniere_validation',
|
|
'derniere_validation_par', 'raison_invalidation', 'proprietaire' ]
|
|
addon_readonly_fields = [ 'type_de_logement',
|
|
'prix_par_mois', 'monnaie', 'surface_en_m2',
|
|
'pays', 'ville', 'position_geographique2',
|
|
'prestations', 'photo', 'description' ]
|
|
list_display = [ 'email', 'etat_validation', 'derniere_publication', 'type_d_offre' ]
|
|
list_filter = [ 'etat_validation', 'type_d_offre', 'type_de_logement',
|
|
'nos_logements' ]
|
|
list_select_related = True
|
|
|
|
def email(self, instance):
|
|
return instance.proprietaire.email
|
|
|
|
def queryset(self, request):
|
|
'''Si l'utilisateur est dans un groupe de validation, on limite les
|
|
annonces visibles à celles qu'il faut valider.
|
|
'''
|
|
qs = super(AnnonceAdmin, self).queryset(request)
|
|
#if request.user.groups.filter(name__contains='validation').exists():
|
|
# qs = qs.filter(etat_validation='attend_validation')
|
|
return qs
|
|
|
|
def get_urls(self):
|
|
urls = super(AnnonceAdmin, self).get_urls()
|
|
my_urls = patterns('',
|
|
url(r'^(?P<object_id>\d+)/valider/$', self.valider),
|
|
url(r'^(?P<object_id>\d+)/invalider/$', self.invalider))
|
|
return my_urls + urls
|
|
|
|
def valider(self, request, object_id=None):
|
|
obj = self.get_object(request, unquote(object_id))
|
|
if '_valider' in request.POST:
|
|
obj.valide(request.user)
|
|
request.record('validation-annonce', 'validation de l\'annonce {annonce}',
|
|
annonce=obj)
|
|
host = request.build_absolute_uri('/')[:-1]
|
|
utils.mail_avec_modele_par_defaut('Annonce valide',
|
|
{'annonce': obj, 'host': host },
|
|
settings.VALIDATION_FROM, obj.proprietaire.email)
|
|
self.message_user(request, _(u'''L'annonce a bien été validée.'''))
|
|
return HttpResponseRedirect('../..')
|
|
return HttpResponseRedirect('..')
|
|
|
|
def invalider(self, request, object_id=None):
|
|
obj = self.get_object(request, unquote(object_id))
|
|
if '_invalider' in request.POST:
|
|
if request.POST.get('raison'):
|
|
request.logger.info(u"refuse l'annonce %s avec la raison « %s »",
|
|
obj.id, request.POST['raison'])
|
|
obj.invalide(request.user, request.POST['raison'])
|
|
request.record('invalidation-annonce', 'invalidation de l\'annonce {annonce} pour la raison {raison}',
|
|
annonce=obj, raison=request.POST['raison'], annonceur=obj.proprietaire)
|
|
host = request.build_absolute_uri('/')[:-1]
|
|
utils.mail_avec_modele_par_defaut('Annonce invalide',
|
|
{'annonce': obj, 'host': host,
|
|
'raison': request.POST['raison'] },
|
|
settings.VALIDATION_FROM, obj.proprietaire.email)
|
|
self.message_user(request, _(u'''L'annonce a bien été invalidée.'''))
|
|
return HttpResponseRedirect('../..')
|
|
else:
|
|
messages.error(request, _(u'''Vous devez donner une raison pour l'invalidation'''))
|
|
return HttpResponseRedirect('..')
|
|
|
|
def position_geographique2(self, instance):
|
|
return instance.osm_wkt()
|
|
position_geographique2.short_description = _(u'Géolocalisation')
|
|
|
|
def change_view(self, request, object_id, extra_context=None):
|
|
obj = self.get_object(request, unquote(object_id))
|
|
|
|
if obj is not None:
|
|
extra_context = {}
|
|
if obj.etat_validation in ('attend_validation', 'invalide'):
|
|
extra_context['validation'] = 1
|
|
extra_context['validation_form'] = ValidationForm()
|
|
extra_context['invalidation_form'] = InvalidationForm()
|
|
|
|
return super(AnnonceAdmin, self).change_view(request, object_id, extra_context=extra_context)
|
|
|
|
class LocalAdminSite(AdminSite):
|
|
exclude = [ 'is_staff' ]
|
|
|
|
def get_urls(self):
|
|
urls = super(LocalAdminSite, self).get_urls()
|
|
new_urls = patterns('',
|
|
url('^parametres/', admin_views.VueParametres.as_view()),
|
|
url('^notifications/', admin_views.VueNotification.as_view()),
|
|
)
|
|
return new_urls + urls
|
|
|
|
class UserCreationForm(django.contrib.auth.forms.UserCreationForm):
|
|
def __init__(self, *args, **kwargs):
|
|
super(UserCreationForm, self).__init__(*args, **kwargs)
|
|
self.fields['groups'].queryset = Group.objects.exclude(name__contains=u'Profil')
|
|
|
|
class Meta:
|
|
fields = ('username', 'groups')
|
|
widgets = { 'groups': FilteredSelectMultiple(_(u'Groupes'), False) }
|
|
|
|
class AdminUserAdmin(UserAdmin):
|
|
add_form = UserCreationForm
|
|
add_fieldsets = (
|
|
(None, {
|
|
'classes': ('wide',),
|
|
'fields': ('username', 'password1', 'password2', 'groups')}
|
|
),
|
|
)
|
|
fieldsets = (('Informations', { 'fields': ('username', 'password', 'email',) }),
|
|
('Authorisations', { 'fields': ('is_active', 'groups') }))
|
|
list_display = [ 'id', 'username', 'email', 'is_active', 'group_list' ]
|
|
list_editable = [ 'username', 'email', 'is_active' ]
|
|
list_filter = [ ('groups', AdministrateurGroupListFilter), 'is_active' ]
|
|
prefetch_related = [ 'groups' ]
|
|
actions = [ actions.export_as_csv ]
|
|
|
|
def group_list(self, instance):
|
|
return u', '.join(instance.groups.values_list('name', flat=True))
|
|
group_list.short_description = _(u'Groupe(s)')
|
|
|
|
def queryset(self, request):
|
|
qs = super(AdminUserAdmin, self).queryset(request)
|
|
return qs.exclude(username__contains='#').filter(is_superuser=False)
|
|
|
|
def save_model(self, request, obj, form, change):
|
|
obj.is_staff = True
|
|
obj.save()
|
|
|
|
class TypeDePrestationAdmin(ModelAdmin):
|
|
list_display = [ 'nom', 'ordre' ]
|
|
list_editable = [ 'ordre' ]
|
|
|
|
contrib_admin.site = LocalAdminSite()
|
|
|
|
contrib_admin.site.register(User, UserAdmin)
|
|
contrib_admin.site.register(Group, GroupAdmin)
|
|
contrib_admin.site.register(models.Administrateur, AdminUserAdmin)
|
|
contrib_admin.site.register(models.EmailModele)
|
|
contrib_admin.site.register(models.Zone, ZoneAdmin)
|
|
contrib_admin.site.register(models.Annonce, AnnonceAdmin)
|
|
contrib_admin.site.register(models.TypeOffre)
|
|
contrib_admin.site.register(models.TypeDePrestation, TypeDePrestationAdmin)
|
|
contrib_admin.site.register(models.ProfilRecherche, ProfilRechercheAdmin)
|
|
contrib_admin.site.register(models.ProfilOffre, ProfilOffreAdmin)
|
|
|
|
import django_journal.admin
|
|
import django_journal
|
|
|
|
class SimpleTagFilter(SimpleListFilter):
|
|
title = _(u'étiquette')
|
|
parameter_name = 'tag'
|
|
|
|
def lookups(self, request, model_admin):
|
|
for tag in django_journal.Tag.objects.filter(journal__isnull=False) \
|
|
.distinct().order_by('name'):
|
|
yield (tag.id, tag.name)
|
|
|
|
def queryset(self, request, queryset):
|
|
if self.value():
|
|
return queryset.filter(tag__id=self.value())
|
|
return queryset
|
|
|
|
class TypeOffreFilter(SimpleListFilter):
|
|
title = _(u"type d'offre")
|
|
parameter_name = 'type-offre'
|
|
|
|
def lookups(self, request, model_admin):
|
|
for type_d_offre in models.TypeOffre.objects.all():
|
|
yield (type_d_offre.id, type_d_offre.nom)
|
|
|
|
def queryset(self, request, queryset):
|
|
if self.value():
|
|
return queryset.for_object(
|
|
models.TypeOffre.objects.get(id=self.value()),
|
|
tag='type_offre')
|
|
return queryset
|
|
|
|
class JournalAdmin(django_journal.admin.JournalAdmin):
|
|
list_filter = (('time', DateRangeFilter),) + \
|
|
tuple(x for x in django_journal.admin.JournalAdmin.list_filter if x != 'tag') + (SimpleTagFilter, TypeOffreFilter)
|
|
|
|
|
|
contrib_admin.site.register(django_journal.Journal, JournalAdmin)
|
|
|
|
# FIXME: chaîne à traduire dans django-daterange-filter
|
|
_('From date')
|
|
_('To date')
|