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