Cleaning de code inutile
[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 = ('poste_nom', 'resume',)
38 list_filter = ('statut', 'est_affiche', )
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',
111 'duree_affectation', 'renumeration',
112 'debut_affectation', 'lieu_affectation', 'nom',
113 'resume', 'date_limite', 'region')
114 fieldsets = (
115 ('Nom', {
116 'fields': ('nom', )
117 }),
118 ('Description générale', {
119 'fields': ('resume','description', 'date_limite', )
120 }),
121 ('Coordonnées', {
122 'fields': ('lieu_affectation', 'bureau', 'region', )
123 }),
124 ('Autre', {
125 'fields': ('debut_affectation', 'duree_affectation',
126 'renumeration', )
127 }),
128 )
129 inlines = []
130 def get_actions(self, request):
131 actions = super(ProxyOffreEmploiAdmin, self).get_actions(request)
132 del actions['affecter_evaluateurs_offre_emploi']
133 return actions
134
135 def response_change(self, request, obj):
136 response = super(ProxyOffreEmploiAdmin, self).response_change(request,
137 obj)
138 user_groupes = request.user.groups.all()
139 if grp_drh_recrutement in user_groupes or \
140 request.user.is_superuser:
141 return HttpResponseRedirect(reverse\
142 ('admin:recrutement_offreemploi_changelist'))
143 return HttpResponseRedirect(reverse\
144 ('admin:recrutement_proxyoffreemploi_changelist'))
145
146 def has_add_permission(self, request):
147 return False
148
149 def has_delete_permission(self, request, obj=None):
150 return False
151
152 def has_change_permission(self, request, obj=None):
153 user_groupes = request.user.groups.all()
154 if grp_evaluateurs_recrutement in user_groupes or \
155 grp_drh_recrutement in user_groupes or \
156 request.user.is_superuser:
157 return True
158 return False
159
160 class CandidatPieceInline(admin.TabularInline):
161 model = CandidatPiece
162 fields = ('candidat', 'nom', 'path',)
163 extra = 1
164 max_num = 3
165
166 class CandidatEvaluationInlineFormSet(BaseInlineFormSet):
167 """
168 Empêche la suppression d'une évaluation pour le CandidatEvaluationInline
169 """
170 def __init__(self, *args, **kwargs):
171 super(CandidatEvaluationInlineFormSet, self).__init__(*args, **kwargs)
172 self.can_delete = False
173
174 class CandidatEvaluationInline(admin.TabularInline):
175 model = CandidatEvaluation
176 fields = ('evaluateur', 'note', 'commentaire')
177 max_num = 0
178 extra = 0
179 formset = CandidatEvaluationInlineFormSet
180
181 def get_readonly_fields(self, request, obj=None):
182 """
183 Empêche la modification des évaluations
184 """
185 if obj:
186 return self.readonly_fields+('evaluateur', 'note', 'commentaire')
187 return self.readonly_fields
188
189 class CandidatAdmin(VersionAdmin):
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 fieldsets = (
196 ("Offre d'emploi", {
197 'fields': ('offre_emploi', )
198 }),
199 ('Informations personnelles', {
200 'fields': ('prenom','nom','genre', 'nationalite',
201 'situation_famille', 'nombre_dependant',)
202 }),
203 ('Coordonnées', {
204 'fields': ('telephone', 'email', 'adresse', 'ville',
205 'etat_province', 'code_postal', 'pays', )
206 }),
207 ('Informations professionnelles', {
208 'fields': ('niveau_diplome','employeur_actuel',
209 'poste_actuel', 'domaine_professionnel',)
210 }),
211 ('Traitement', {
212 'fields': ('statut', )
213 }),
214 )
215 inlines = [
216 CandidatPieceInline,
217 CandidatEvaluationInline,
218 ]
219
220 actions = ['envoyer_courriel_candidats']
221
222 def get_actions(self, request):
223 actions = super(CandidatAdmin, self).get_actions(request)
224 del actions['delete_selected']
225 return actions
226
227 # Envoyer un courriel à des candidats
228 def envoyer_courriel_candidats(modeladmin, obj, candidats):
229 selected = obj.POST.getlist(admin.ACTION_CHECKBOX_NAME)
230
231 return HttpResponseRedirect(reverse('selectionner_template')+
232 "?ids=%s" % (",".join(selected)))
233 envoyer_courriel_candidats.short_description = u'Envoyer courriel'
234
235 # Évaluer un candidat
236 def evaluer_candidat(self, obj):
237 return "<a href='%s?candidat__id__exact=%s'>Évaluer le candidat</a>" % \
238 (reverse('admin:recrutement_candidatevaluation_changelist'),
239 obj.id)
240 evaluer_candidat.allow_tags = True
241 evaluer_candidat.short_description = 'Évaluation'
242
243 # Afficher un candidat
244 def afficher_candidat(self, obj):
245 return "<a href='%s'>Voir le candidat</a>" % \
246 (reverse('admin:recrutement_proxycandidat_change', args=(obj.id,)))
247 afficher_candidat.allow_tags = True
248 afficher_candidat.short_description = u'Détails du candidat'
249
250 # Voir l'offre d'emploi
251 def voir_offre_emploi(self, obj):
252 return "<a href='%s'>Voir l'offre d'emploi</a>" % \
253 (reverse('admin:recrutement_proxyoffreemploi_change',
254 args=(obj.offre_emploi.id,)))
255 voir_offre_emploi.allow_tags = True
256 voir_offre_emploi.short_description = "Afficher l'offre d'emploi"
257
258 # Calculer la moyenne des notes
259 def calculer_moyenne(self, obj):
260 evaluations = CandidatEvaluation.objects.filter(candidat=obj)
261 offre_emploi = obj.offre_emploi
262
263 notes = [evaluation.note for evaluation in evaluations.all() \
264 if evaluation.note is not None]
265
266 if len(notes) > 0 and offre_emploi.date_limite <= datetime.date.today():
267 moyenne_votes = float(sum(notes)) / len(notes)
268 else:
269 moyenne_votes = "Non disponible"
270 return moyenne_votes
271 calculer_moyenne.allow_tags = True
272 calculer_moyenne.short_description = "Moyenne des notes"
273
274 def add_delete_permission(self, request, obj=None) :
275 user_groupes = request.user.groups.all()
276 if grp_drh_recrutement in user_groupes or \
277 grp_administrateurs_recrutement in user_groupes or \
278 request.user.is_superuser:
279 return True
280 return False
281
282 def has_add_permission(self, request):
283 return self.add_delete_permission(request, request)
284
285 def has_delete_permission(self, request, obj=None):
286 return self.add_delete_permission(request, request)
287
288 def has_change_permission(self, request, obj=None):
289 user_groupes = request.user.groups.all()
290 if grp_drh_recrutement in user_groupes or \
291 grp_administrateurs_recrutement in user_groupes or \
292 request.user.is_superuser:
293 return True
294 return False
295
296 def queryset(self, request):
297 """
298 Spécifie un queryset limité, autrement Django exécute un
299 select_related() sans paramètre, ce qui a pour effet de charger tous
300 les objets FK, sans limite de profondeur. Dès qu'on arrive, dans les
301 modèles de Region, il existe plusieurs boucles, ce qui conduit à la
302 génération d'une requête infinie.
303
304 """
305 qs = self.model._default_manager.get_query_set()
306 # Si user est superuser afficher tous les candidats
307 user_groupes = request.user.groups.all()
308 if not grp_drh_recrutement in user_groupes and \
309 not request.user.is_superuser:
310 # Si le user n'est ni un évaluateur ni un administrateur régional,
311 # retourner none
312
313 # Vérifier groupes
314 if grp_evaluateurs_recrutement in user_groupes:
315 try:
316 user = Evaluateur.objects.get(user=request.user)
317 except Evaluateur.DoesNotExist:
318 return qs.none()
319 """
320 elif grp_administrateurs_recrutement in user_groupes:
321 try:
322 user = AdministrateurRegional.objects.get(user=obj.user)
323 except AdministrateurRegional.DoesNotExist:
324 return qs.none()
325 """
326 else:
327 return qs.none()
328 ids = [c.id for c in user.candidats.all()]
329 return qs.select_related('candidats').filter(id__in=ids)
330 return qs.select_related('candidats')
331
332 class ProxyCandidatAdmin(CandidatAdmin):
333 readonly_fields = ('statut', 'offre_emploi', 'prenom', 'nom',
334 'genre', 'nationalite', 'situation_famille',
335 'nombre_dependant', 'telephone', 'email', 'adresse',
336 'ville', 'etat_province', 'code_postal', 'pays',
337 'niveau_diplome', 'employeur_actuel', 'poste_actuel',
338 'domaine_professionnel', 'pieces_jointes',)
339 fieldsets = (
340 ("Offre d'emploi", {
341 'fields': ('offre_emploi', )
342 }),
343 ('Informations personnelles', {
344 'fields': ('prenom','nom','genre', 'nationalite',
345 'situation_famille', 'nombre_dependant',)
346 }),
347 ('Coordonnées', {
348 'fields': ('telephone', 'email', 'adresse', 'ville',
349 'etat_province', 'code_postal', 'pays', )
350 }),
351 ('Informations professionnelles', {
352 'fields': ('niveau_diplome','employeur_actuel',
353 'poste_actuel', 'domaine_professionnel',)
354 }),
355 )
356 inlines = []
357
358 def response_change(self, request, obj):
359 response = super(ProxyCandidatAdmin, self).response_change(request, obj)
360 user_groupes = request.user.groups.all()
361 if grp_drh_recrutement in user_groupes or \
362 request.user.is_superuser:
363 return HttpResponseRedirect(reverse\
364 ('admin:recrutement_candidat_changelist'))
365 return HttpResponseRedirect(reverse\
366 ('admin:recrutement_proxycandidat_changelist'))
367
368 def has_add_permission(self, request):
369 return False
370
371 def has_delete_permission(self, request, obj=None):
372 return False
373
374 def has_change_permission(self, request, obj=None):
375 user_groupes = request.user.groups.all()
376 if grp_drh_recrutement in user_groupes or \
377 grp_administrateurs_recrutement in user_groupes or \
378 grp_evaluateurs_recrutement in user_groupes or \
379 request.user.is_superuser:
380 return True
381 return False
382
383 class CandidatPieceAdmin(admin.ModelAdmin):
384 list_display = ('nom', 'candidat', )
385
386 def queryset(self, request):
387 """
388 Spécifie un queryset limité, autrement Django exécute un
389 select_related() sans paramètre, ce qui a pour effet de charger tous
390 les objets FK, sans limite de profondeur. Dès qu'on arrive, dans les
391 modèles de Region, il existe plusieurs boucles, ce qui conduit à la
392 génération d'une requête infinie.
393 Affiche la liste de candidats que si le user connecté
394 possède un Evaluateur
395 """
396 qs = self.model._default_manager.get_query_set()
397 return qs.select_related('candidat')
398
399 class EvaluateurAdmin(VersionAdmin):
400 fieldsets = (
401 ("Utilisateur", {
402 'fields': ('user',)
403 }),
404 ("Offres d'emploi à évaluer", {
405 'fields': ('offres_emploi',)
406 }),
407 )
408
409 def get_actions(self, request):
410 actions = super(EvaluateurAdmin, self).get_actions(request)
411 del actions['delete_selected']
412 return actions
413
414 class AdministrateurRegionalAdmin(VersionAdmin):
415 pass
416
417 class CandidatEvaluationAdmin(VersionAdmin):
418 list_display = ('_candidat', '_offre_emploi', 'evaluateur', '_note',
419 '_commentaire', )
420 readonly_fields = ('candidat', 'evaluateur')
421 fieldsets = (
422 ('Évaluation du candidat', {
423 'fields': ('candidat', 'evaluateur', 'note', 'commentaire', )
424 }),
425 )
426
427 def get_actions(self, request):
428 actions = super(CandidatEvaluationAdmin, self).get_actions(request)
429 del actions['delete_selected']
430 return actions
431
432 def _note(self, obj):
433 """
434 Si l'évaluateur n'a pas encore donné de note au candidat, indiquer
435 un lien pour Évaluer le candidat.
436 Sinon afficher la note.
437 """
438 evaluateur = obj.evaluateur
439 candidat = obj.candidat
440 candidat_evaluation = CandidatEvaluation.objects.\
441 get(candidat=candidat, evaluateur=evaluateur)
442 if obj.note is None:
443 return "<a href='%s'>Candidat non évalué</a>" % \
444 (reverse('admin:recrutement_candidatevaluation_change',
445 args=(candidat_evaluation.id,)))
446 return "<a href='%s'>%s</a>" % \
447 (reverse('admin:recrutement_candidatevaluation_change',
448 args=(candidat_evaluation.id,)), obj.note)
449 return
450 _note.allow_tags = True
451 _note.short_description = "Votre 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)