1829: Réorganiser les urls pour recrutement
[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 119
b8778e64
NBV
120 def get_actions(self, request):
121 actions = super(ProxyOffreEmploiAdmin, self).get_actions(request)
122 del actions['affecter_evaluateurs_offre_emploi']
123 return actions
124
a029f641
NBV
125 def response_change(self, request, obj):
126 response = super(ProxyOffreEmploiAdmin, self).response_change(request, obj)
127 user_groupes = request.user.groups.all()
128 if grp_drh_recrutement in user_groupes or \
129 request.user.is_superuser:
130 return HttpResponseRedirect(reverse('admin:recrutement_offreemploi_changelist'))
131 return HttpResponseRedirect(reverse('admin:recrutement_proxyoffreemploi_changelist'))
132
f6724c20
NBV
133 def has_add_permission(self, request):
134 return False
135
136 def has_delete_permission(self, request, obj=None):
137 return False
138
2d083449
NBV
139 def has_change_permission(self, request, obj=None):
140 user_groupes = request.user.groups.all()
beef7690 141 if grp_evaluateurs_recrutement in user_groupes or \
d46075cb
NBV
142 grp_drh_recrutement in user_groupes or \
143 request.user.is_superuser:
2d083449
NBV
144 return True
145 return False
146
540dfae4
NBV
147 """class ProxyEvaluateur(Evaluateur.candidats.through):
148
cced6a23 149 Ce proxy sert uniquement dans l'admin à disposer d'un libellé
150 plus ergonomique.
540dfae4 151
cced6a23 152 class Meta:
153 proxy = True
154 verbose_name = "évaluateur"
540dfae4 155 """
572c8d08
NBV
156class CandidatPieceInline(admin.TabularInline):
157 model = CandidatPiece
158 fields = ('candidat', 'nom', 'path',)
159 extra = 1
160 max_num = 3
161
572c8d08
NBV
162class CandidatEvaluationInlineFormSet(BaseInlineFormSet):
163 """
164 Empêche la suppression d'une évaluation pour le CandidatEvaluationInline
165 """
166 def __init__(self, *args, **kwargs):
167 super(CandidatEvaluationInlineFormSet, self).__init__(*args, **kwargs)
168 self.can_delete = False
169
170class CandidatEvaluationInline(admin.TabularInline):
171 model = CandidatEvaluation
172 fields = ('evaluateur', 'note', 'commentaire')
173 max_num = 0
174 extra = 0
175 formset = CandidatEvaluationInlineFormSet
176
177 def get_readonly_fields(self, request, obj=None):
178 """
179 Empêche la modification des évaluations
180 """
181 if obj:
182 return self.readonly_fields+('evaluateur', 'note', 'commentaire')
183 return self.readonly_fields
184
185
d2b30f5f 186class CandidatAdmin(VersionAdmin):
7f9e891e 187 date_hierarchy = 'date_creation'
0fd8a26d 188 list_display = ('nom', 'prenom', 'offre_emploi','statut',
dc7faf2b
NBV
189 'voir_offre_emploi', 'calculer_moyenne',
190 'afficher_candidat',)
8ea41642 191 list_filter = ('offre_emploi', )
7f9e891e 192 fieldsets = (
4896b661 193 ("Offre d'emploi", {
194 'fields': ('offre_emploi', )
195 }),
7f9e891e 196 ('Informations personnelles', {
4fd0c4a2 197 'fields': ('prenom','nom','genre', 'nationalite',
7f9e891e 198 'situation_famille', 'nombre_dependant',)
199 }),
ec517164 200 ('Coordonnées', {
201 'fields': ('telephone', 'email', 'adresse', 'ville',
202 'etat_province', 'code_postal', 'pays', )
7f9e891e 203 }),
204 ('Informations professionnelles', {
4896b661 205 'fields': ('niveau_diplome','employeur_actuel',
8ea41642 206 'poste_actuel', 'domaine_professionnel',)
7f9e891e 207 }),
65c4cbd9
NBV
208 ('Traitement', {
209 'fields': ('statut', )
7f9e891e 210 }),
211 )
170c9aa2 212 inlines = [
213 CandidatPieceInline,
572c8d08 214 CandidatEvaluationInline,
170c9aa2 215 ]
f9983b5a 216
540dfae4 217 actions = ['envoyer_courriel_candidats']
a029f641
NBV
218
219 def get_actions(self, request):
220 actions = super(CandidatAdmin, self).get_actions(request)
221 del actions['delete_selected']
540dfae4 222 return actions
362a3534 223
4e8340cf 224 # Envoyer un courriel à des candidats
52765380 225 def envoyer_courriel_candidats(modeladmin, obj, candidats):
226 selected = obj.POST.getlist(admin.ACTION_CHECKBOX_NAME)
227
32834000 228 return HttpResponseRedirect(reverse('selectionner_template')+
52765380 229 "?ids=%s" % (",".join(selected)))
230 envoyer_courriel_candidats.short_description = u'Envoyer courriel'
231
05503d56 232 # Évaluer un candidat
596fe324 233 def evaluer_candidat(self, obj):
dc7faf2b 234 return "<a href='%s?candidat__id__exact=%s'>Évaluer le candidat</a>" % \
beef7690
NBV
235 (reverse('admin:recrutement_candidatevaluation_changelist'),
236 obj.id)
596fe324 237 evaluer_candidat.allow_tags = True
beef7690 238 evaluer_candidat.short_description = 'Évaluation'
596fe324 239
7d82fd33 240 # Afficher un candidat
241 def afficher_candidat(self, obj):
382501c1
NBV
242 return "<a href='%s'>Voir le candidat</a>" % \
243 (reverse('admin:recrutement_proxycandidat_change', args=(obj.id,)))
7d82fd33 244 afficher_candidat.allow_tags = True
8e0d552d 245 afficher_candidat.short_description = u'Détails du candidat'
7d82fd33 246
8941aee7 247 # Voir l'offre d'emploi
248 def voir_offre_emploi(self, obj):
720c3ad5
NBV
249 return "<a href='%s'>Voir l'offre d'emploi</a>" % \
250 (reverse('admin:recrutement_proxyoffreemploi_change',
251 args=(obj.offre_emploi.id,)))
8941aee7 252 voir_offre_emploi.allow_tags = True
253 voir_offre_emploi.short_description = "Afficher l'offre d'emploi"
254
8941aee7 255 # Calculer la moyenne des notes
256 def calculer_moyenne(self, obj):
257 evaluations = CandidatEvaluation.objects.filter(candidat=obj)
258 offre_emploi = obj.offre_emploi
259
f6724c20
NBV
260 notes = [evaluation.note for evaluation in evaluations.all() \
261 if evaluation.note is not None]
8941aee7 262
263 if len(notes) > 0 and offre_emploi.date_limite <= datetime.date.today():
264 moyenne_votes = float(sum(notes)) / len(notes)
265 else:
266 moyenne_votes = "Non disponible"
267 return moyenne_votes
268 calculer_moyenne.allow_tags = True
269 calculer_moyenne.short_description = "Moyenne des notes"
270
f6724c20
NBV
271 def add_delete_permission(self, request, obj=None) :
272 user_groupes = request.user.groups.all()
273 if grp_drh_recrutement in user_groupes or \
d46075cb
NBV
274 grp_administrateurs_recrutement in user_groupes or \
275 request.user.is_superuser:
f6724c20
NBV
276 return True
277 return False
4896b661 278
f6724c20
NBV
279 def has_add_permission(self, request):
280 return self.add_delete_permission(request, request)
4896b661 281
f6724c20
NBV
282 def has_delete_permission(self, request, obj=None):
283 return self.add_delete_permission(request, request)
284
285 def has_change_permission(self, request, obj=None):
286 user_groupes = request.user.groups.all()
287 if grp_drh_recrutement in user_groupes or \
d46075cb
NBV
288 grp_administrateurs_recrutement in user_groupes or \
289 request.user.is_superuser:
f6724c20
NBV
290 return True
291 return False
4896b661 292
d46075cb 293 def queryset(self, request):
f9983b5a 294 """
8ea41642 295 Spécifie un queryset limité, autrement Django exécute un
296 select_related() sans paramètre, ce qui a pour effet de charger tous
297 les objets FK, sans limite de profondeur. Dès qu'on arrive, dans les
298 modèles de Region, il existe plusieurs boucles, ce qui conduit à la
299 génération d'une requête infinie.
d835c9f3 300
f9983b5a 301 """
f6724c20
NBV
302 qs = self.model._default_manager.get_query_set()
303 # Si user est superuser afficher tous les candidats
d46075cb
NBV
304 user_groupes = request.user.groups.all()
305 if not grp_drh_recrutement in user_groupes and \
306 not request.user.is_superuser:
f6724c20
NBV
307 # Si le user n'est ni un évaluateur ni un administrateur régional,
308 # retourner none
309
310 # Vérifier groupes
311 if grp_evaluateurs_recrutement in user_groupes:
ca73c3c6 312 try:
2c3c54ee 313 user = Evaluateur.objects.get(user=request.user)
ca73c3c6
NBV
314 except Evaluateur.DoesNotExist:
315 return qs.none()
efedc086
NBV
316 """
317 elif grp_administrateurs_recrutement in user_groupes:
318 try:
319 user = AdministrateurRegional.objects.get(user=obj.user)
320 except AdministrateurRegional.DoesNotExist:
321 return qs.none()
322 """
f6724c20
NBV
323 else:
324 return qs.none()
f6724c20
NBV
325 ids = [c.id for c in user.candidats.all()]
326 return qs.select_related('candidats').filter(id__in=ids)
327 return qs.select_related('candidats')
328
329class ProxyCandidatAdmin(CandidatAdmin):
f6724c20 330 readonly_fields = ('statut', 'offre_emploi', 'prenom', 'nom',
4fd0c4a2
NBV
331 'genre', 'nationalite', 'situation_famille',
332 'nombre_dependant', 'telephone', 'email', 'adresse',
333 'ville', 'etat_province', 'code_postal', 'pays',
334 'niveau_diplome', 'employeur_actuel', 'poste_actuel',
8e0d552d 335 'domaine_professionnel', 'pieces_jointes',)
2d083449
NBV
336 fieldsets = (
337 ("Offre d'emploi", {
338 'fields': ('offre_emploi', )
339 }),
340 ('Informations personnelles', {
4fd0c4a2 341 'fields': ('prenom','nom','genre', 'nationalite',
2d083449
NBV
342 'situation_famille', 'nombre_dependant',)
343 }),
344 ('Coordonnées', {
345 'fields': ('telephone', 'email', 'adresse', 'ville',
346 'etat_province', 'code_postal', 'pays', )
347 }),
348 ('Informations professionnelles', {
349 'fields': ('niveau_diplome','employeur_actuel',
350 'poste_actuel', 'domaine_professionnel',)
8e0d552d 351 }),
2d083449 352 )
f6724c20 353 inlines = []
2d083449 354
a029f641
NBV
355 def response_change(self, request, obj):
356 response = super(ProxyCandidatAdmin, self).response_change(request, obj)
357 user_groupes = request.user.groups.all()
358 if grp_drh_recrutement in user_groupes or \
359 request.user.is_superuser:
360 return HttpResponseRedirect(reverse('admin:recrutement_candidat_changelist'))
361 return HttpResponseRedirect(reverse('admin:recrutement_proxycandidat_changelist'))
362
f6724c20
NBV
363 def has_add_permission(self, request):
364 return False
365
366 def has_delete_permission(self, request, obj=None):
367 return False
2adf9e0c 368
2d083449
NBV
369 def has_change_permission(self, request, obj=None):
370 user_groupes = request.user.groups.all()
beef7690
NBV
371 if grp_drh_recrutement in user_groupes or \
372 grp_administrateurs_recrutement in user_groupes or \
d46075cb
NBV
373 grp_evaluateurs_recrutement in user_groupes or \
374 request.user.is_superuser:
2d083449
NBV
375 return True
376 return False
377
2e9ee615 378class CandidatPieceAdmin(admin.ModelAdmin):
170c9aa2 379 list_display = ('nom', 'candidat', )
380
381 def queryset(self, request):
382 """
383 Spécifie un queryset limité, autrement Django exécute un
384 select_related() sans paramètre, ce qui a pour effet de charger tous
385 les objets FK, sans limite de profondeur. Dès qu'on arrive, dans les
386 modèles de Region, il existe plusieurs boucles, ce qui conduit à la
387 génération d'une requête infinie.
d835c9f3 388 Affiche la liste de candidats que si le user connecté
27c81d11 389 possède un Evaluateur
170c9aa2 390 """
391 qs = self.model._default_manager.get_query_set()
392 return qs.select_related('candidat')
2e9ee615 393
d2b30f5f 394class EvaluateurAdmin(VersionAdmin):
eb579d40 395 fieldsets = (
540dfae4
NBV
396 ("Utilisateur", {
397 'fields': ('user',)
398 }),
399 ("Offres d'emploi à évaluer", {
400 'fields': ('offres_emploi',)
401 }),
eb579d40 402 )
4418c732 403
a029f641
NBV
404 def get_actions(self, request):
405 actions = super(EvaluateurAdmin, self).get_actions(request)
406 del actions['delete_selected']
540dfae4 407 return actions
a029f641 408
b89fef74 409class AdministrateurRegionalAdmin(VersionAdmin):
27c81d11 410 pass
411
d2b30f5f 412class CandidatEvaluationAdmin(VersionAdmin):
2c3c54ee 413 list_display = ('_candidat', '_offre_emploi', 'evaluateur', '_note',
b903198b 414 '_commentaire', )
beef7690
NBV
415 readonly_fields = ('candidat', 'evaluateur')
416 fieldsets = (
417 ('Évaluation du candidat', {
418 'fields': ('candidat', 'evaluateur', 'note', 'commentaire', )
419 }),
420 )
421
a029f641
NBV
422 def get_actions(self, request):
423 actions = super(CandidatEvaluationAdmin, self).get_actions(request)
424 del actions['delete_selected']
540dfae4 425 return actions
a029f641 426
beef7690
NBV
427 def _note(self, obj):
428 """
429 Si l'évaluateur n'a pas encore donné de note au candidat, indiquer
430 un lien pour Évaluer le candidat.
431 Sinon afficher la note.
432 """
b903198b
NBV
433 evaluateur = obj.evaluateur
434 candidat = obj.candidat
435 candidat_evaluation = CandidatEvaluation.objects.\
436 get(candidat=candidat, evaluateur=evaluateur)
beef7690 437 if obj.note is None:
b903198b 438 return "<a href='%s'>Candidat non évalué</a>" % \
beef7690 439 (reverse('admin:recrutement_candidatevaluation_change',
b903198b
NBV
440 args=(candidat_evaluation.id,)))
441 return "<a href='%s'>%s</a>" % \
442 (reverse('admin:recrutement_candidatevaluation_change',
443 args=(candidat_evaluation.id,)), obj.note)
444 return
beef7690
NBV
445 _note.allow_tags = True
446 _note.short_description = "Votre note"
447 _note.admin_order_field = 'note'
448
449 def _candidat(self, obj):
450 return "<a href='%s'>%s</a>" \
451 % (reverse('admin:recrutement_proxycandidat_change',
452 args=(obj.candidat.id,)), obj.candidat)
453 _candidat.allow_tags = True
454 _candidat.short_description = 'Candidat'
455
456 def _commentaire(self, obj):
457 """
458 Si l'évaluateur n'a pas encore donné de note au candidat, indiquer
459 dans le champ commentaire, Aucun au lieu de (None)
460 Sinon afficher la note.
461 """
462 if obj.commentaire is None:
463 return "Aucun"
464 return obj.commentaire
465 _commentaire.allow_tags = True
466 _commentaire.short_description = "Commentaire"
720c3ad5 467
720c3ad5 468
beef7690
NBV
469 def _offre_emploi(self, obj):
470 return "<a href='%s'>%s</a>" % \
471 (reverse('admin:recrutement_proxyoffreemploi_change',
dc7faf2b 472 args=(obj.candidat.offre_emploi.id,)), obj.candidat.offre_emploi)
beef7690
NBV
473 _offre_emploi.allow_tags = True
474 _offre_emploi.short_description = "Voir offre d'emploi"
beef7690 475
21b02da5
NBV
476 def has_change_permission(self, request, obj=None):
477 """
478 Permettre la visualisation dans la changelist
479 mais interdire l'accès à modifier l'objet si l'évaluateur n'est pas
480 le request.user
481 """
482 return obj is None or request.user == obj.evaluateur.user
483
720c3ad5 484 def queryset(self, request):
beef7690
NBV
485 """
486 Afficher uniquement les évaluations de l'évaluateur, sauf si
487 l'utilisateur est super admin.
488 """
720c3ad5 489 qs = self.model._default_manager.get_query_set()
beef7690 490 user_groupes = request.user.groups.all()
d46075cb
NBV
491 if grp_drh_recrutement in user_groupes or \
492 request.user.is_superuser:
beef7690
NBV
493 return qs.select_related('offre_emploi')
494
ca73c3c6 495 try:
d069cdf1 496 evaluateur = Evaluateur.objects.get(user=request.user)
ca73c3c6
NBV
497 except Evaluateur.DoesNotExist:
498 return qs.none()
499
500 candidats_evaluations = CandidatEvaluation.objects.\
501 filter(evaluateur=evaluateur)
502 candidats_evaluations_ids = [ce.id for ce in \
503 candidats_evaluations.all()]
504 return qs.select_related('offre_emploi').\
505 filter(id__in=candidats_evaluations_ids)
596fe324 506
4e8340cf 507class CourrielTemplateAdmin(VersionAdmin):
a029f641
NBV
508 def get_actions(self, request):
509 actions = super(CourrielTemplateAdmin, self).get_actions(request)
510 del actions['delete_selected']
540dfae4 511 return actions
4e8340cf 512
df59fcab 513admin.site.register(OffreEmploi, OffreEmploiAdmin)
382501c1 514admin.site.register(ProxyOffreEmploi, ProxyOffreEmploiAdmin)
df59fcab 515admin.site.register(Candidat, CandidatAdmin)
382501c1 516admin.site.register(ProxyCandidat, ProxyCandidatAdmin)
720c3ad5 517admin.site.register(CandidatEvaluation, CandidatEvaluationAdmin)
27c81d11 518admin.site.register(Evaluateur, EvaluateurAdmin)
beef7690 519#admin.site.register(AdministrateurRegional, AdministrateurRegionalAdmin)
32834000 520admin.site.register(CourrielTemplate, CourrielTemplateAdmin)