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