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