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
CommitLineData
df59fcab 1# -*- encoding: utf-8 -*-
2
6067184b 3from django.core.urlresolvers import reverse
4from django.http import HttpResponseRedirect
df59fcab 5from django.contrib import admin
38df74bb 6from django.shortcuts import get_object_or_404
8e0d552d 7from django.core.files.storage import default_storage
38df74bb 8
6067184b 9from reversion.admin import VersionAdmin
38df74bb 10from datamaster_modeles.models import Employe, Implantation, Region
572c8d08 11from django.forms.models import BaseInlineFormSet
6067184b 12
df59fcab 13from recrutement.models import *
f6724c20
NBV
14from recrutement.workflow import grp_administrateurs_recrutement,\
15 grp_evaluateurs_recrutement, grp_drh_recrutement
a084e988 16from recrutement.forms import *
df59fcab 17
a029f641
NBV
18"""
19class MetaAdmin(VersionAdmin):
20 def get_actions(self, request):
21
22Pour refactoring
23"""
d2b30f5f 24class OffreEmploiAdmin(VersionAdmin):
7f9e891e 25 date_hierarchy = 'date_creation'
4fd0c4a2
NBV
26 list_display = ('nom', 'resume', 'date_limite', 'region', 'statut',
27 'est_affiche', '_candidatsList')
514db699 28 list_filter = ('statut', 'est_affiche', )
c4874d66 29 actions = ['affecter_evaluateurs_offre_emploi', ]
a084e988 30 form = OffreEmploiForm
2c3c54ee 31
a029f641
NBV
32 def get_actions(self, request):
33 actions = super(OffreEmploiAdmin, self).get_actions(request)
34 del actions['delete_selected']
540dfae4 35 return actions
a029f641 36
c4874d66
NBV
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
8941aee7 45 # Afficher la liste des candidats pour l'offre d'emploi
596fe324 46 def _candidatsList(self, obj):
8ea41642 47 return "<a href='%s?offre_emploi__id__exact=%s'>Voir les candidats \
48 </a>" % (reverse('admin:recrutement_candidat_changelist'), obj.id)
2f78949d 49 _candidatsList.allow_tags = True
f6724c20 50 _candidatsList.short_description = "Afficher la liste des candidats"
362a3534 51
c4874d66 52
2f78949d 53 def queryset(self, request):
382501c1 54 qs = self.model._default_manager.get_query_set()
f6724c20
NBV
55 # Si user est superuser afficher toutes les offres d'emploi
56 user_groupes = request.user.groups.all()
d46075cb
NBV
57 if not grp_drh_recrutement in user_groupes and \
58 not request.user.is_superuser:
beef7690 59 """
beef7690
NBV
60 Si le user n'est ni un évaluateur ni un administrateur régional,
61 retourner none
62 Vérifier groupes
63 """
f6724c20 64 if grp_evaluateurs_recrutement in user_groupes:
ca73c3c6
NBV
65 try:
66 user = Evaluateur.objects.get(user=request.user)
67 except Evaluateur.DoesNotExist:
68 return qs.none()
f6724c20 69 elif grp_administrateurs_recrutement in user_groupes:
ca73c3c6
NBV
70 try:
71 user = AdministrateurRegional.objects.get(user=request.user)
72 except AdministrateurRegional.DoesNotExist:
73 return qs.none()
f6724c20
NBV
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)
beef7690 81 if type(user) is Evaluateur:
f6724c20 82 candidats = [g for g in user.candidats.all()]
beef7690 83 offre_emploi_ids = [c.offre_emploi.id for c in candidats]
f6724c20 84 return qs.select_related('offre_emploi').\
beef7690 85 filter(id__in=offre_emploi_ids)
3bcef02d 86 return qs.none()
382501c1 87 return qs.select_related('offre_emploi')
f6724c20
NBV
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 \
d46075cb
NBV
92 grp_administrateurs_recrutement in user_groupes or \
93 request.user.is_superuser:
f6724c20
NBV
94 return True
95 return False
96
97class ProxyOffreEmploiAdmin(OffreEmploiAdmin):
4fd0c4a2
NBV
98 list_display = ('nom', 'resume', 'date_limite', 'region', 'statut',
99 'est_affiche')
14e06ff6 100 readonly_fields = ('description', 'bureau',
f6724c20
NBV
101 'duree_affectation', 'renumeration',
102 'debut_affectation', 'lieu_affectation', 'nom',
103 'resume', 'date_limite', 'region')
104 fieldsets = (
720c3ad5 105 ('Nom', {
f6724c20
NBV
106 'fields': ('nom', )
107 }),
720c3ad5 108 ('Description générale', {
14e06ff6 109 'fields': ('resume','description', 'date_limite', )
f6724c20 110 }),
720c3ad5 111 ('Coordonnées', {
f6724c20
NBV
112 'fields': ('lieu_affectation', 'bureau', 'region', )
113 }),
720c3ad5 114 ('Autre', {
f6724c20
NBV
115 'fields': ('debut_affectation', 'duree_affectation',
116 'renumeration', )
117 }),
118 )
a029f641
NBV
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
f6724c20
NBV
128 def has_add_permission(self, request):
129 return False
130
131 def has_delete_permission(self, request, obj=None):
132 return False
133
2d083449
NBV
134 def has_change_permission(self, request, obj=None):
135 user_groupes = request.user.groups.all()
beef7690 136 if grp_evaluateurs_recrutement in user_groupes or \
d46075cb
NBV
137 grp_drh_recrutement in user_groupes or \
138 request.user.is_superuser:
2d083449
NBV
139 return True
140 return False
141
540dfae4
NBV
142 """class ProxyEvaluateur(Evaluateur.candidats.through):
143
cced6a23 144 Ce proxy sert uniquement dans l'admin à disposer d'un libellé
145 plus ergonomique.
540dfae4 146
cced6a23 147 class Meta:
148 proxy = True
149 verbose_name = "évaluateur"
540dfae4 150 """
572c8d08
NBV
151class CandidatPieceInline(admin.TabularInline):
152 model = CandidatPiece
153 fields = ('candidat', 'nom', 'path',)
154 extra = 1
155 max_num = 3
156
572c8d08
NBV
157class 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
165class 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
d2b30f5f 181class CandidatAdmin(VersionAdmin):
7f9e891e 182 date_hierarchy = 'date_creation'
0fd8a26d 183 list_display = ('nom', 'prenom', 'offre_emploi','statut',
dc7faf2b
NBV
184 'voir_offre_emploi', 'calculer_moyenne',
185 'afficher_candidat',)
8ea41642 186 list_filter = ('offre_emploi', )
7f9e891e 187 fieldsets = (
4896b661 188 ("Offre d'emploi", {
189 'fields': ('offre_emploi', )
190 }),
7f9e891e 191 ('Informations personnelles', {
4fd0c4a2 192 'fields': ('prenom','nom','genre', 'nationalite',
7f9e891e 193 'situation_famille', 'nombre_dependant',)
194 }),
ec517164 195 ('Coordonnées', {
196 'fields': ('telephone', 'email', 'adresse', 'ville',
197 'etat_province', 'code_postal', 'pays', )
7f9e891e 198 }),
199 ('Informations professionnelles', {
4896b661 200 'fields': ('niveau_diplome','employeur_actuel',
8ea41642 201 'poste_actuel', 'domaine_professionnel',)
7f9e891e 202 }),
65c4cbd9
NBV
203 ('Traitement', {
204 'fields': ('statut', )
7f9e891e 205 }),
206 )
170c9aa2 207 inlines = [
208 CandidatPieceInline,
572c8d08 209 CandidatEvaluationInline,
170c9aa2 210 ]
f9983b5a 211
540dfae4 212 actions = ['envoyer_courriel_candidats']
a029f641
NBV
213
214 def get_actions(self, request):
215 actions = super(CandidatAdmin, self).get_actions(request)
216 del actions['delete_selected']
540dfae4 217 return actions
362a3534 218
4e8340cf 219 # Envoyer un courriel à des candidats
52765380 220 def envoyer_courriel_candidats(modeladmin, obj, candidats):
221 selected = obj.POST.getlist(admin.ACTION_CHECKBOX_NAME)
222
32834000 223 return HttpResponseRedirect(reverse('selectionner_template')+
52765380 224 "?ids=%s" % (",".join(selected)))
225 envoyer_courriel_candidats.short_description = u'Envoyer courriel'
226
05503d56 227 # Évaluer un candidat
596fe324 228 def evaluer_candidat(self, obj):
dc7faf2b 229 return "<a href='%s?candidat__id__exact=%s'>Évaluer le candidat</a>" % \
beef7690
NBV
230 (reverse('admin:recrutement_candidatevaluation_changelist'),
231 obj.id)
596fe324 232 evaluer_candidat.allow_tags = True
beef7690 233 evaluer_candidat.short_description = 'Évaluation'
596fe324 234
7d82fd33 235 # Afficher un candidat
236 def afficher_candidat(self, obj):
382501c1
NBV
237 return "<a href='%s'>Voir le candidat</a>" % \
238 (reverse('admin:recrutement_proxycandidat_change', args=(obj.id,)))
7d82fd33 239 afficher_candidat.allow_tags = True
8e0d552d 240 afficher_candidat.short_description = u'Détails du candidat'
7d82fd33 241
8941aee7 242 # Voir l'offre d'emploi
243 def voir_offre_emploi(self, obj):
720c3ad5
NBV
244 return "<a href='%s'>Voir l'offre d'emploi</a>" % \
245 (reverse('admin:recrutement_proxyoffreemploi_change',
246 args=(obj.offre_emploi.id,)))
8941aee7 247 voir_offre_emploi.allow_tags = True
248 voir_offre_emploi.short_description = "Afficher l'offre d'emploi"
249
8941aee7 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
f6724c20
NBV
255 notes = [evaluation.note for evaluation in evaluations.all() \
256 if evaluation.note is not None]
8941aee7 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
f6724c20
NBV
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 \
d46075cb
NBV
269 grp_administrateurs_recrutement in user_groupes or \
270 request.user.is_superuser:
f6724c20
NBV
271 return True
272 return False
4896b661 273
f6724c20
NBV
274 def has_add_permission(self, request):
275 return self.add_delete_permission(request, request)
4896b661 276
f6724c20
NBV
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 \
d46075cb
NBV
283 grp_administrateurs_recrutement in user_groupes or \
284 request.user.is_superuser:
f6724c20
NBV
285 return True
286 return False
4896b661 287
d46075cb 288 def queryset(self, request):
f9983b5a 289 """
8ea41642 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.
d835c9f3 295
f9983b5a 296 """
f6724c20
NBV
297 qs = self.model._default_manager.get_query_set()
298 # Si user est superuser afficher tous les candidats
d46075cb
NBV
299 user_groupes = request.user.groups.all()
300 if not grp_drh_recrutement in user_groupes and \
301 not request.user.is_superuser:
f6724c20
NBV
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:
ca73c3c6 307 try:
2c3c54ee 308 user = Evaluateur.objects.get(user=request.user)
ca73c3c6
NBV
309 except Evaluateur.DoesNotExist:
310 return qs.none()
efedc086
NBV
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 """
f6724c20
NBV
318 else:
319 return qs.none()
f6724c20
NBV
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
324class ProxyCandidatAdmin(CandidatAdmin):
f6724c20 325 readonly_fields = ('statut', 'offre_emploi', 'prenom', 'nom',
4fd0c4a2
NBV
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',
8e0d552d 330 'domaine_professionnel', 'pieces_jointes',)
2d083449
NBV
331 fieldsets = (
332 ("Offre d'emploi", {
333 'fields': ('offre_emploi', )
334 }),
335 ('Informations personnelles', {
4fd0c4a2 336 'fields': ('prenom','nom','genre', 'nationalite',
2d083449
NBV
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',)
8e0d552d 346 }),
2d083449 347 )
f6724c20 348 inlines = []
2d083449 349
a029f641
NBV
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
f6724c20
NBV
358 def has_add_permission(self, request):
359 return False
360
361 def has_delete_permission(self, request, obj=None):
362 return False
2adf9e0c 363
2d083449
NBV
364 def has_change_permission(self, request, obj=None):
365 user_groupes = request.user.groups.all()
beef7690
NBV
366 if grp_drh_recrutement in user_groupes or \
367 grp_administrateurs_recrutement in user_groupes or \
d46075cb
NBV
368 grp_evaluateurs_recrutement in user_groupes or \
369 request.user.is_superuser:
2d083449
NBV
370 return True
371 return False
372
2e9ee615 373class CandidatPieceAdmin(admin.ModelAdmin):
170c9aa2 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.
d835c9f3 383 Affiche la liste de candidats que si le user connecté
27c81d11 384 possède un Evaluateur
170c9aa2 385 """
386 qs = self.model._default_manager.get_query_set()
387 return qs.select_related('candidat')
2e9ee615 388
d2b30f5f 389class EvaluateurAdmin(VersionAdmin):
eb579d40 390 fieldsets = (
540dfae4
NBV
391 ("Utilisateur", {
392 'fields': ('user',)
393 }),
394 ("Offres d'emploi à évaluer", {
395 'fields': ('offres_emploi',)
396 }),
eb579d40 397 )
4418c732 398
a029f641
NBV
399 def get_actions(self, request):
400 actions = super(EvaluateurAdmin, self).get_actions(request)
401 del actions['delete_selected']
540dfae4 402 return actions
a029f641 403
b89fef74 404class AdministrateurRegionalAdmin(VersionAdmin):
27c81d11 405 pass
406
d2b30f5f 407class CandidatEvaluationAdmin(VersionAdmin):
2c3c54ee 408 list_display = ('_candidat', '_offre_emploi', 'evaluateur', '_note',
b903198b 409 '_commentaire', )
beef7690
NBV
410 readonly_fields = ('candidat', 'evaluateur')
411 fieldsets = (
412 ('Évaluation du candidat', {
413 'fields': ('candidat', 'evaluateur', 'note', 'commentaire', )
414 }),
415 )
416
a029f641
NBV
417 def get_actions(self, request):
418 actions = super(CandidatEvaluationAdmin, self).get_actions(request)
419 del actions['delete_selected']
540dfae4 420 return actions
a029f641 421
beef7690
NBV
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 """
b903198b
NBV
428 evaluateur = obj.evaluateur
429 candidat = obj.candidat
430 candidat_evaluation = CandidatEvaluation.objects.\
431 get(candidat=candidat, evaluateur=evaluateur)
beef7690 432 if obj.note is None:
b903198b 433 return "<a href='%s'>Candidat non évalué</a>" % \
beef7690 434 (reverse('admin:recrutement_candidatevaluation_change',
b903198b
NBV
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
beef7690
NBV
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"
720c3ad5 462
720c3ad5 463
beef7690
NBV
464 def _offre_emploi(self, obj):
465 return "<a href='%s'>%s</a>" % \
466 (reverse('admin:recrutement_proxyoffreemploi_change',
dc7faf2b 467 args=(obj.candidat.offre_emploi.id,)), obj.candidat.offre_emploi)
beef7690
NBV
468 _offre_emploi.allow_tags = True
469 _offre_emploi.short_description = "Voir offre d'emploi"
beef7690 470
21b02da5
NBV
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
720c3ad5 479 def queryset(self, request):
beef7690
NBV
480 """
481 Afficher uniquement les évaluations de l'évaluateur, sauf si
482 l'utilisateur est super admin.
483 """
720c3ad5 484 qs = self.model._default_manager.get_query_set()
beef7690 485 user_groupes = request.user.groups.all()
d46075cb
NBV
486 if grp_drh_recrutement in user_groupes or \
487 request.user.is_superuser:
beef7690
NBV
488 return qs.select_related('offre_emploi')
489
ca73c3c6 490 try:
d069cdf1 491 evaluateur = Evaluateur.objects.get(user=request.user)
ca73c3c6
NBV
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)
596fe324 501
4e8340cf 502class CourrielTemplateAdmin(VersionAdmin):
a029f641
NBV
503 def get_actions(self, request):
504 actions = super(CourrielTemplateAdmin, self).get_actions(request)
505 del actions['delete_selected']
540dfae4 506 return actions
4e8340cf 507
df59fcab 508admin.site.register(OffreEmploi, OffreEmploiAdmin)
382501c1 509admin.site.register(ProxyOffreEmploi, ProxyOffreEmploiAdmin)
df59fcab 510admin.site.register(Candidat, CandidatAdmin)
382501c1 511admin.site.register(ProxyCandidat, ProxyCandidatAdmin)
720c3ad5 512admin.site.register(CandidatEvaluation, CandidatEvaluationAdmin)
27c81d11 513admin.site.register(Evaluateur, EvaluateurAdmin)
beef7690 514#admin.site.register(AdministrateurRegional, AdministrateurRegionalAdmin)
32834000 515admin.site.register(CourrielTemplate, CourrielTemplateAdmin)