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