1764:champ genre pour courriel automatique
[auf_rh_dae.git] / project / recrutement / admin.py
CommitLineData
df59fcab 1# -*- encoding: utf-8 -*-
2
6067184b 3from django.core.urlresolvers import reverse
4from django.http import HttpResponseRedirect
df59fcab 5from django.contrib import admin
38df74bb 6from django.shortcuts import get_object_or_404
8e0d552d 7from django.core.files.storage import default_storage
38df74bb 8
6067184b 9from reversion.admin import VersionAdmin
38df74bb 10from datamaster_modeles.models import Employe, Implantation, Region
572c8d08 11from django.forms.models import BaseInlineFormSet
6067184b 12
df59fcab 13from recrutement.models import *
f6724c20
NBV
14from recrutement.workflow import grp_administrateurs_recrutement,\
15 grp_evaluateurs_recrutement, grp_drh_recrutement
df59fcab 16
d2b30f5f 17class OffreEmploiAdmin(VersionAdmin):
7f9e891e 18 date_hierarchy = 'date_creation'
4fd0c4a2
NBV
19 list_display = ('nom', 'resume', 'date_limite', 'region', 'statut',
20 'est_affiche', '_candidatsList')
514db699 21 list_filter = ('statut', 'est_affiche', )
c4874d66 22 actions = ['affecter_evaluateurs_offre_emploi', ]
2c3c54ee 23
c4874d66
NBV
24 # Affecter un évaluateurs à des offres d'emploi
25 def affecter_evaluateurs_offre_emploi(modeladmin, obj, candidats):
26 selected = obj.POST.getlist(admin.ACTION_CHECKBOX_NAME)
27
28 return HttpResponseRedirect(reverse('affecter_evaluateurs_offre_emploi')+
29 "?ids=%s" % (",".join(selected)))
30 affecter_evaluateurs_offre_emploi.short_description = u'Affecter évaluateur(s)'
31
8941aee7 32 # Afficher la liste des candidats pour l'offre d'emploi
596fe324 33 def _candidatsList(self, obj):
8ea41642 34 return "<a href='%s?offre_emploi__id__exact=%s'>Voir les candidats \
35 </a>" % (reverse('admin:recrutement_candidat_changelist'), obj.id)
2f78949d 36 _candidatsList.allow_tags = True
f6724c20 37 _candidatsList.short_description = "Afficher la liste des candidats"
362a3534 38
c4874d66 39
2f78949d 40 def queryset(self, request):
382501c1 41 qs = self.model._default_manager.get_query_set()
f6724c20
NBV
42 # Si user est superuser afficher toutes les offres d'emploi
43 user_groupes = request.user.groups.all()
d46075cb
NBV
44 if not grp_drh_recrutement in user_groupes and \
45 not request.user.is_superuser:
beef7690 46 """
beef7690
NBV
47 Si le user n'est ni un évaluateur ni un administrateur régional,
48 retourner none
49 Vérifier groupes
50 """
f6724c20 51 if grp_evaluateurs_recrutement in user_groupes:
ca73c3c6
NBV
52 try:
53 user = Evaluateur.objects.get(user=request.user)
54 except Evaluateur.DoesNotExist:
55 return qs.none()
f6724c20 56 elif grp_administrateurs_recrutement in user_groupes:
ca73c3c6
NBV
57 try:
58 user = AdministrateurRegional.objects.get(user=request.user)
59 except AdministrateurRegional.DoesNotExist:
60 return qs.none()
f6724c20
NBV
61 else:
62 return qs.none()
63
64 if type(user) is AdministrateurRegional:
65 region_ids = [g.id for g in user.regions.all()]
66 return qs.select_related('offre_emploi').\
67 filter(region__in=region_ids)
beef7690 68 if type(user) is Evaluateur:
f6724c20 69 candidats = [g for g in user.candidats.all()]
beef7690 70 offre_emploi_ids = [c.offre_emploi.id for c in candidats]
f6724c20 71 return qs.select_related('offre_emploi').\
beef7690 72 filter(id__in=offre_emploi_ids)
3bcef02d 73 return qs.none()
382501c1 74 return qs.select_related('offre_emploi')
f6724c20
NBV
75
76 def has_change_permission(self, request, obj=None):
77 user_groupes = request.user.groups.all()
78 if grp_drh_recrutement in user_groupes or \
d46075cb
NBV
79 grp_administrateurs_recrutement in user_groupes or \
80 request.user.is_superuser:
f6724c20
NBV
81 return True
82 return False
83
84class ProxyOffreEmploiAdmin(OffreEmploiAdmin):
4fd0c4a2
NBV
85 list_display = ('nom', 'resume', 'date_limite', 'region', 'statut',
86 'est_affiche')
14e06ff6 87 readonly_fields = ('description', 'bureau',
f6724c20
NBV
88 'duree_affectation', 'renumeration',
89 'debut_affectation', 'lieu_affectation', 'nom',
90 'resume', 'date_limite', 'region')
91 fieldsets = (
720c3ad5 92 ('Nom', {
f6724c20
NBV
93 'fields': ('nom', )
94 }),
720c3ad5 95 ('Description générale', {
14e06ff6 96 'fields': ('resume','description', 'date_limite', )
f6724c20 97 }),
720c3ad5 98 ('Coordonnées', {
f6724c20
NBV
99 'fields': ('lieu_affectation', 'bureau', 'region', )
100 }),
720c3ad5 101 ('Autre', {
f6724c20
NBV
102 'fields': ('debut_affectation', 'duree_affectation',
103 'renumeration', )
104 }),
105 )
106 def has_add_permission(self, request):
107 return False
108
109 def has_delete_permission(self, request, obj=None):
110 return False
111
2d083449
NBV
112 def has_change_permission(self, request, obj=None):
113 user_groupes = request.user.groups.all()
beef7690 114 if grp_evaluateurs_recrutement in user_groupes or \
d46075cb
NBV
115 grp_drh_recrutement in user_groupes or \
116 request.user.is_superuser:
2d083449
NBV
117 return True
118 return False
119
27c81d11 120class ProxyEvaluateur(Evaluateur.candidats.through):
cced6a23 121 """
122 Ce proxy sert uniquement dans l'admin à disposer d'un libellé
123 plus ergonomique.
124 """
125 class Meta:
126 proxy = True
127 verbose_name = "évaluateur"
128
572c8d08
NBV
129class CandidatPieceInline(admin.TabularInline):
130 model = CandidatPiece
131 fields = ('candidat', 'nom', 'path',)
132 extra = 1
133 max_num = 3
134
eb579d40 135class EvaluateurInline(admin.TabularInline):
cced6a23 136 model = ProxyEvaluateur
720c3ad5 137 fields = ('evaluateur',)
eb579d40 138 extra = 1
139
572c8d08
NBV
140
141class CandidatEvaluationInlineFormSet(BaseInlineFormSet):
142 """
143 Empêche la suppression d'une évaluation pour le CandidatEvaluationInline
144 """
145 def __init__(self, *args, **kwargs):
146 super(CandidatEvaluationInlineFormSet, self).__init__(*args, **kwargs)
147 self.can_delete = False
148
149class CandidatEvaluationInline(admin.TabularInline):
150 model = CandidatEvaluation
151 fields = ('evaluateur', 'note', 'commentaire')
152 max_num = 0
153 extra = 0
154 formset = CandidatEvaluationInlineFormSet
155
156 def get_readonly_fields(self, request, obj=None):
157 """
158 Empêche la modification des évaluations
159 """
160 if obj:
161 return self.readonly_fields+('evaluateur', 'note', 'commentaire')
162 return self.readonly_fields
163
164
d2b30f5f 165class CandidatAdmin(VersionAdmin):
7f9e891e 166 date_hierarchy = 'date_creation'
0fd8a26d 167 list_display = ('nom', 'prenom', 'offre_emploi','statut',
dc7faf2b
NBV
168 'voir_offre_emploi', 'calculer_moyenne',
169 'afficher_candidat',)
8ea41642 170 list_filter = ('offre_emploi', )
7f9e891e 171 fieldsets = (
4896b661 172 ("Offre d'emploi", {
173 'fields': ('offre_emploi', )
174 }),
7f9e891e 175 ('Informations personnelles', {
4fd0c4a2 176 'fields': ('prenom','nom','genre', 'nationalite',
7f9e891e 177 'situation_famille', 'nombre_dependant',)
178 }),
ec517164 179 ('Coordonnées', {
180 'fields': ('telephone', 'email', 'adresse', 'ville',
181 'etat_province', 'code_postal', 'pays', )
7f9e891e 182 }),
183 ('Informations professionnelles', {
4896b661 184 'fields': ('niveau_diplome','employeur_actuel',
8ea41642 185 'poste_actuel', 'domaine_professionnel',)
7f9e891e 186 }),
65c4cbd9
NBV
187 ('Traitement', {
188 'fields': ('statut', )
7f9e891e 189 }),
190 )
170c9aa2 191 inlines = [
192 CandidatPieceInline,
eb579d40 193 EvaluateurInline,
572c8d08 194 CandidatEvaluationInline,
170c9aa2 195 ]
f9983b5a 196
4e8340cf 197 actions = ['affecter_candidats_evaluateur', 'envoyer_courriel_candidats']
362a3534 198 # Affecter un évaluateurs à des candidats
7061f835 199 def affecter_candidats_evaluateur(modeladmin, obj, candidats):
596fe324 200 selected = obj.POST.getlist(admin.ACTION_CHECKBOX_NAME)
2adf9e0c 201
8ea41642 202 return HttpResponseRedirect(reverse('affecter_evaluateurs_candidats')+
203 "?ids=%s" % (",".join(selected)))
c4874d66 204 affecter_candidats_evaluateur.short_description = u'Affecter évaluateur(s)'
362a3534 205
4e8340cf 206 # Envoyer un courriel à des candidats
52765380 207 def envoyer_courriel_candidats(modeladmin, obj, candidats):
208 selected = obj.POST.getlist(admin.ACTION_CHECKBOX_NAME)
209
32834000 210 return HttpResponseRedirect(reverse('selectionner_template')+
52765380 211 "?ids=%s" % (",".join(selected)))
212 envoyer_courriel_candidats.short_description = u'Envoyer courriel'
213
05503d56 214 # Évaluer un candidat
596fe324 215 def evaluer_candidat(self, obj):
dc7faf2b 216 return "<a href='%s?candidat__id__exact=%s'>Évaluer le candidat</a>" % \
beef7690
NBV
217 (reverse('admin:recrutement_candidatevaluation_changelist'),
218 obj.id)
596fe324 219 evaluer_candidat.allow_tags = True
beef7690 220 evaluer_candidat.short_description = 'Évaluation'
596fe324 221
7d82fd33 222 # Afficher un candidat
223 def afficher_candidat(self, obj):
382501c1
NBV
224 return "<a href='%s'>Voir le candidat</a>" % \
225 (reverse('admin:recrutement_proxycandidat_change', args=(obj.id,)))
7d82fd33 226 afficher_candidat.allow_tags = True
8e0d552d 227 afficher_candidat.short_description = u'Détails du candidat'
7d82fd33 228
8941aee7 229 # Voir l'offre d'emploi
230 def voir_offre_emploi(self, obj):
720c3ad5
NBV
231 return "<a href='%s'>Voir l'offre d'emploi</a>" % \
232 (reverse('admin:recrutement_proxyoffreemploi_change',
233 args=(obj.offre_emploi.id,)))
8941aee7 234 voir_offre_emploi.allow_tags = True
235 voir_offre_emploi.short_description = "Afficher l'offre d'emploi"
236
8941aee7 237 # Calculer la moyenne des notes
238 def calculer_moyenne(self, obj):
239 evaluations = CandidatEvaluation.objects.filter(candidat=obj)
240 offre_emploi = obj.offre_emploi
241
f6724c20
NBV
242 notes = [evaluation.note for evaluation in evaluations.all() \
243 if evaluation.note is not None]
8941aee7 244
245 if len(notes) > 0 and offre_emploi.date_limite <= datetime.date.today():
246 moyenne_votes = float(sum(notes)) / len(notes)
247 else:
248 moyenne_votes = "Non disponible"
249 return moyenne_votes
250 calculer_moyenne.allow_tags = True
251 calculer_moyenne.short_description = "Moyenne des notes"
252
f6724c20
NBV
253 def add_delete_permission(self, request, obj=None) :
254 user_groupes = request.user.groups.all()
255 if grp_drh_recrutement in user_groupes or \
d46075cb
NBV
256 grp_administrateurs_recrutement in user_groupes or \
257 request.user.is_superuser:
f6724c20
NBV
258 return True
259 return False
4896b661 260
f6724c20
NBV
261 def has_add_permission(self, request):
262 return self.add_delete_permission(request, request)
4896b661 263
f6724c20
NBV
264 def has_delete_permission(self, request, obj=None):
265 return self.add_delete_permission(request, request)
266
267 def has_change_permission(self, request, obj=None):
268 user_groupes = request.user.groups.all()
269 if grp_drh_recrutement in user_groupes or \
d46075cb
NBV
270 grp_administrateurs_recrutement in user_groupes or \
271 request.user.is_superuser:
f6724c20
NBV
272 return True
273 return False
4896b661 274
d46075cb 275 def queryset(self, request):
f9983b5a 276 """
8ea41642 277 Spécifie un queryset limité, autrement Django exécute un
278 select_related() sans paramètre, ce qui a pour effet de charger tous
279 les objets FK, sans limite de profondeur. Dès qu'on arrive, dans les
280 modèles de Region, il existe plusieurs boucles, ce qui conduit à la
281 génération d'une requête infinie.
d835c9f3 282
f9983b5a 283 """
f6724c20
NBV
284 qs = self.model._default_manager.get_query_set()
285 # Si user est superuser afficher tous les candidats
d46075cb
NBV
286 user_groupes = request.user.groups.all()
287 if not grp_drh_recrutement in user_groupes and \
288 not request.user.is_superuser:
f6724c20
NBV
289 # Si le user n'est ni un évaluateur ni un administrateur régional,
290 # retourner none
291
292 # Vérifier groupes
293 if grp_evaluateurs_recrutement in user_groupes:
ca73c3c6 294 try:
2c3c54ee 295 user = Evaluateur.objects.get(user=request.user)
ca73c3c6
NBV
296 except Evaluateur.DoesNotExist:
297 return qs.none()
efedc086
NBV
298 """
299 elif grp_administrateurs_recrutement in user_groupes:
300 try:
301 user = AdministrateurRegional.objects.get(user=obj.user)
302 except AdministrateurRegional.DoesNotExist:
303 return qs.none()
304 """
f6724c20
NBV
305 else:
306 return qs.none()
f6724c20
NBV
307 ids = [c.id for c in user.candidats.all()]
308 return qs.select_related('candidats').filter(id__in=ids)
309 return qs.select_related('candidats')
310
311class ProxyCandidatAdmin(CandidatAdmin):
f6724c20 312 readonly_fields = ('statut', 'offre_emploi', 'prenom', 'nom',
4fd0c4a2
NBV
313 'genre', 'nationalite', 'situation_famille',
314 'nombre_dependant', 'telephone', 'email', 'adresse',
315 'ville', 'etat_province', 'code_postal', 'pays',
316 'niveau_diplome', 'employeur_actuel', 'poste_actuel',
8e0d552d 317 'domaine_professionnel', 'pieces_jointes',)
2d083449
NBV
318 fieldsets = (
319 ("Offre d'emploi", {
320 'fields': ('offre_emploi', )
321 }),
322 ('Informations personnelles', {
4fd0c4a2 323 'fields': ('prenom','nom','genre', 'nationalite',
2d083449
NBV
324 'situation_famille', 'nombre_dependant',)
325 }),
326 ('Coordonnées', {
327 'fields': ('telephone', 'email', 'adresse', 'ville',
328 'etat_province', 'code_postal', 'pays', )
329 }),
330 ('Informations professionnelles', {
331 'fields': ('niveau_diplome','employeur_actuel',
332 'poste_actuel', 'domaine_professionnel',)
8e0d552d 333 }),
2d083449 334 )
f6724c20 335 inlines = []
2d083449 336
f6724c20
NBV
337 def has_add_permission(self, request):
338 return False
339
340 def has_delete_permission(self, request, obj=None):
341 return False
2adf9e0c 342
2d083449
NBV
343 def has_change_permission(self, request, obj=None):
344 user_groupes = request.user.groups.all()
beef7690
NBV
345 if grp_drh_recrutement in user_groupes or \
346 grp_administrateurs_recrutement in user_groupes or \
d46075cb
NBV
347 grp_evaluateurs_recrutement in user_groupes or \
348 request.user.is_superuser:
2d083449
NBV
349 return True
350 return False
351
2e9ee615 352class CandidatPieceAdmin(admin.ModelAdmin):
170c9aa2 353 list_display = ('nom', 'candidat', )
354
355 def queryset(self, request):
356 """
357 Spécifie un queryset limité, autrement Django exécute un
358 select_related() sans paramètre, ce qui a pour effet de charger tous
359 les objets FK, sans limite de profondeur. Dès qu'on arrive, dans les
360 modèles de Region, il existe plusieurs boucles, ce qui conduit à la
361 génération d'une requête infinie.
d835c9f3 362 Affiche la liste de candidats que si le user connecté
27c81d11 363 possède un Evaluateur
170c9aa2 364 """
365 qs = self.model._default_manager.get_query_set()
366 return qs.select_related('candidat')
2e9ee615 367
d2b30f5f 368class EvaluateurAdmin(VersionAdmin):
eb579d40 369 fieldsets = (
f6724c20 370 (None, {'fields': ('user', )}),
720c3ad5 371 #(None, {'fields': ('candidats',)}),
eb579d40 372 )
4418c732 373
b89fef74 374class AdministrateurRegionalAdmin(VersionAdmin):
27c81d11 375 pass
376
d2b30f5f 377class CandidatEvaluationAdmin(VersionAdmin):
2c3c54ee 378 list_display = ('_candidat', '_offre_emploi', 'evaluateur', '_note',
b903198b 379 '_commentaire', )
beef7690
NBV
380 readonly_fields = ('candidat', 'evaluateur')
381 fieldsets = (
382 ('Évaluation du candidat', {
383 'fields': ('candidat', 'evaluateur', 'note', 'commentaire', )
384 }),
385 )
386
387 def _note(self, obj):
388 """
389 Si l'évaluateur n'a pas encore donné de note au candidat, indiquer
390 un lien pour Évaluer le candidat.
391 Sinon afficher la note.
392 """
b903198b
NBV
393 evaluateur = obj.evaluateur
394 candidat = obj.candidat
395 candidat_evaluation = CandidatEvaluation.objects.\
396 get(candidat=candidat, evaluateur=evaluateur)
beef7690 397 if obj.note is None:
b903198b 398 return "<a href='%s'>Candidat non évalué</a>" % \
beef7690 399 (reverse('admin:recrutement_candidatevaluation_change',
b903198b
NBV
400 args=(candidat_evaluation.id,)))
401 return "<a href='%s'>%s</a>" % \
402 (reverse('admin:recrutement_candidatevaluation_change',
403 args=(candidat_evaluation.id,)), obj.note)
404 return
beef7690
NBV
405 _note.allow_tags = True
406 _note.short_description = "Votre note"
407 _note.admin_order_field = 'note'
408
409 def _candidat(self, obj):
410 return "<a href='%s'>%s</a>" \
411 % (reverse('admin:recrutement_proxycandidat_change',
412 args=(obj.candidat.id,)), obj.candidat)
413 _candidat.allow_tags = True
414 _candidat.short_description = 'Candidat'
415
416 def _commentaire(self, obj):
417 """
418 Si l'évaluateur n'a pas encore donné de note au candidat, indiquer
419 dans le champ commentaire, Aucun au lieu de (None)
420 Sinon afficher la note.
421 """
422 if obj.commentaire is None:
423 return "Aucun"
424 return obj.commentaire
425 _commentaire.allow_tags = True
426 _commentaire.short_description = "Commentaire"
720c3ad5 427
720c3ad5 428
beef7690
NBV
429 def _offre_emploi(self, obj):
430 return "<a href='%s'>%s</a>" % \
431 (reverse('admin:recrutement_proxyoffreemploi_change',
dc7faf2b 432 args=(obj.candidat.offre_emploi.id,)), obj.candidat.offre_emploi)
beef7690
NBV
433 _offre_emploi.allow_tags = True
434 _offre_emploi.short_description = "Voir offre d'emploi"
beef7690 435
21b02da5
NBV
436 def has_change_permission(self, request, obj=None):
437 """
438 Permettre la visualisation dans la changelist
439 mais interdire l'accès à modifier l'objet si l'évaluateur n'est pas
440 le request.user
441 """
442 return obj is None or request.user == obj.evaluateur.user
443
720c3ad5 444 def queryset(self, request):
beef7690
NBV
445 """
446 Afficher uniquement les évaluations de l'évaluateur, sauf si
447 l'utilisateur est super admin.
448 """
720c3ad5 449 qs = self.model._default_manager.get_query_set()
beef7690 450 user_groupes = request.user.groups.all()
d46075cb
NBV
451 if grp_drh_recrutement in user_groupes or \
452 request.user.is_superuser:
beef7690
NBV
453 return qs.select_related('offre_emploi')
454
ca73c3c6 455 try:
d069cdf1 456 evaluateur = Evaluateur.objects.get(user=request.user)
ca73c3c6
NBV
457 except Evaluateur.DoesNotExist:
458 return qs.none()
459
460 candidats_evaluations = CandidatEvaluation.objects.\
461 filter(evaluateur=evaluateur)
462 candidats_evaluations_ids = [ce.id for ce in \
463 candidats_evaluations.all()]
464 return qs.select_related('offre_emploi').\
465 filter(id__in=candidats_evaluations_ids)
596fe324 466
4e8340cf 467class CourrielTemplateAdmin(VersionAdmin):
468 pass
469
df59fcab 470admin.site.register(OffreEmploi, OffreEmploiAdmin)
382501c1 471admin.site.register(ProxyOffreEmploi, ProxyOffreEmploiAdmin)
df59fcab 472admin.site.register(Candidat, CandidatAdmin)
382501c1 473admin.site.register(ProxyCandidat, ProxyCandidatAdmin)
720c3ad5 474admin.site.register(CandidatEvaluation, CandidatEvaluationAdmin)
27c81d11 475admin.site.register(Evaluateur, EvaluateurAdmin)
beef7690 476#admin.site.register(AdministrateurRegional, AdministrateurRegionalAdmin)
32834000 477admin.site.register(CourrielTemplate, CourrielTemplateAdmin)