This repository has been archived on 2023-02-21. You can view files and clone it, but cannot push or open issues or pull requests.
dauphine-logement/appli_project/appli_socle/admin.py

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')