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