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