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