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