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