Merge remote-tracking branch 'origin/recrutement' into recrutement
[auf_rh_dae.git] / project / recrutement / admin.py
CommitLineData
df59fcab 1# -*- encoding: utf-8 -*-
2
1b4c8020 3import textwrap
4ba84959 4
75f0e87b 5from django.conf import settings
df59fcab 6from django.contrib import admin
75f0e87b 7from django.core.urlresolvers import reverse
97b168d6 8from django.db.models import Avg
0b9dea96
PP
9from django.shortcuts import render_to_response
10from django.template import RequestContext
38df74bb 11
0b9dea96 12from auf.django.emploi.models import STATUT_CHOICES
75f0e87b
DB
13from django.forms.models import BaseInlineFormSet
14from django.http import HttpResponseRedirect
b31ce2d7 15from django.shortcuts import redirect
6067184b 16from reversion.admin import VersionAdmin
7d9736ba 17
e949324b 18from auf.django.emploi.models import OffreEmploi, Candidat, CandidatPiece
4c17e518 19from auf.django.references.models import Region, Bureau
7d9736ba 20
afd3be54 21from project.groups import get_employe_from_user as get_emp
17c90428 22from project.rh import models as rh
3bc5ea42 23
b31ce2d7 24from project.recrutement.forms import OffreEmploiForm
86e4bb65
DB
25from project.recrutement.groups import \
26 grp_drh, grp_drh2, \
27 grp_directeurs_bureau, \
28 grp_administrateurs, \
29 grp_correspondants_rh, \
30 grp_haute_direction
b31ce2d7 31from project.recrutement.models import \
4ba84959
EMS
32 Evaluateur, CandidatEvaluation, \
33 ProxyOffreEmploi, ProxyCandidat, MesCandidatEvaluation, \
b31ce2d7 34 CourrielTemplate
8bf241db 35
df59fcab 36
7d9736ba 37### CONSTANTES
d333c690
NBV
38IMPLANTATIONS_CENTRALES = [15, 19]
39
b31ce2d7 40
08a9b6fc
EMS
41class BaseAdmin(admin.ModelAdmin):
42
43 class Media:
44 css = {'screen': ('css/admin_custom.css',)}
45
46
97b168d6
OL
47class OrderedChangeList(admin.views.main.ChangeList):
48 """
49 Surcharge pour appliquer le order_by d'un annotate
50 """
51 def get_query_set(self):
52 qs = super(OrderedChangeList, self).get_query_set()
53 qs = qs.order_by('-moyenne')
54 return qs
55
b31ce2d7 56
08a9b6fc 57class OffreEmploiAdmin(BaseAdmin, VersionAdmin):
7f9e891e 58 date_hierarchy = 'date_creation'
b31ce2d7
EMS
59 list_display = (
60 'nom', 'date_limite', 'region', 'statut', 'est_affiche',
61 '_candidatsList'
62 )
7d0ae1ba
NBV
63 exclude = ('actif', 'poste_nom', 'resume',)
64 list_filter = ('statut',)
c4874d66 65 actions = ['affecter_evaluateurs_offre_emploi', ]
a084e988 66 form = OffreEmploiForm
2c3c54ee 67
7d9736ba 68 ### Actions à afficher
a029f641
NBV
69 def get_actions(self, request):
70 actions = super(OffreEmploiAdmin, self).get_actions(request)
71 del actions['delete_selected']
540dfae4 72 return actions
a029f641 73
7d9736ba 74 ### Affecter un évaluateurs à des offres d'emploi
b31ce2d7 75 def affecter_evaluateurs_offre_emploi(modeladmin, obj, candidats):
c4874d66
NBV
76 selected = obj.POST.getlist(admin.ACTION_CHECKBOX_NAME)
77
b31ce2d7
EMS
78 return HttpResponseRedirect(
79 reverse('affecter_evaluateurs_offre_emploi') +
80 "?ids=%s" % (",".join(selected))
81 )
82
83 affecter_evaluateurs_offre_emploi.short_description = \
84 u'Affecter évaluateur(s)'
c4874d66 85
7d9736ba 86 ### Afficher la liste des candidats pour l'offre d'emploi
b31ce2d7 87 def _candidatsList(self, obj):
8ea41642 88 return "<a href='%s?offre_emploi__id__exact=%s'>Voir les candidats \
89 </a>" % (reverse('admin:recrutement_candidat_changelist'), obj.id)
b31ce2d7 90 _candidatsList.allow_tags = True
f6724c20 91 _candidatsList.short_description = "Afficher la liste des candidats"
362a3534 92
7d9736ba 93 ### Formulaire
3a542b85
NBV
94 def get_form(self, request, obj=None, **kwargs):
95 form = super(OffreEmploiAdmin, self).get_form(request, obj, **kwargs)
96 employe = get_emp(request.user)
3a542b85 97 user_groupes = request.user.groups.all()
b31ce2d7 98
7d9736ba 99 # Region
b31ce2d7 100 if 'region' in form.declared_fields:
d333c690
NBV
101 region_field = form.declared_fields['region']
102 else:
103 region_field = form.base_fields['region']
104
86e4bb65
DB
105 if grp_drh in user_groupes or \
106 grp_drh2 in user_groupes or \
107 grp_haute_direction in user_groupes:
d333c690
NBV
108 region_field.queryset = Region.objects.all()
109 else:
110 region_field.queryset = Region.objects.\
111 filter(id=employe.implantation.region.id)
b31ce2d7 112
7d9736ba 113 # Poste
b31ce2d7 114 if 'poste' in form.declared_fields:
d333c690
NBV
115 poste_field = form.declared_fields['poste']
116 else:
117 poste_field = form.base_fields['poste']
118
86e4bb65
DB
119 if grp_drh in user_groupes or \
120 grp_drh2 in user_groupes or \
121 grp_haute_direction in user_groupes:
d333c690
NBV
122 poste_field.queryset = rh.Poste.objects.all()
123 else:
7d9736ba
NBV
124 poste_field.queryset = rh.Poste.objects.\
125 filter(implantation__region=employe.implantation.region).\
126 exclude(implantation__in=IMPLANTATIONS_CENTRALES)
b31ce2d7 127
7d9736ba 128 # Bureau
b31ce2d7 129 if 'bureau' in form.declared_fields:
d333c690
NBV
130 bureau_field = form.declared_fields['bureau']
131 else:
132 bureau_field = form.base_fields['bureau']
133
86e4bb65
DB
134 if grp_drh in user_groupes or \
135 grp_drh2 in user_groupes or \
136 grp_haute_direction in user_groupes:
d333c690
NBV
137 bureau_field.queryset = Bureau.objects.all()
138 else:
b31ce2d7
EMS
139 bureau_field.queryset = \
140 Bureau.objects.filter(region=employe.implantation.region)
141
3a542b85 142 return form
b31ce2d7 143
7d9736ba 144 ### Queryset
b31ce2d7 145
2f78949d 146 def queryset(self, request):
b31ce2d7
EMS
147 qs = self.model._default_manager.get_query_set() \
148 .select_related('offre_emploi')
f6724c20 149 user_groupes = request.user.groups.all()
86e4bb65
DB
150 if grp_drh in user_groupes or \
151 grp_drh2 in user_groupes or \
152 grp_haute_direction in user_groupes:
940c9dd8 153 return qs
f6724c20 154
86e4bb65
DB
155 if grp_directeurs_bureau in user_groupes or \
156 grp_correspondants_rh in user_groupes or \
157 grp_administrateurs in user_groupes:
7d9736ba 158 employe = get_emp(request.user)
940c9dd8 159 return qs.filter(region=employe.implantation.region)
7d9736ba 160
c69ecb4f 161 if Evaluateur.objects.filter(user=request.user).exists():
940c9dd8 162 evaluateur = Evaluateur.objects.get(user=request.user)
b31ce2d7
EMS
163 offre_ids = [
164 e.candidat.offre_emploi_id
165 for e in CandidatEvaluation.objects
166 .select_related('candidat')
167 .filter(evaluateur=evaluateur)
168 ]
940c9dd8
OL
169 return qs.filter(id__in=offre_ids)
170
7d9736ba
NBV
171 return qs.none()
172
173 ### Permission add, delete, change
3a542b85
NBV
174 def has_add_permission(self, request):
175 user_groupes = request.user.groups.all()
26add0fd 176 if request.user.is_superuser is True or \
86e4bb65
DB
177 grp_drh in user_groupes or \
178 grp_drh2 in user_groupes or \
179 grp_directeurs_bureau in user_groupes or \
180 grp_administrateurs in user_groupes or \
181 grp_haute_direction in user_groupes:
3a542b85 182 return True
b31ce2d7 183 return False
3a542b85 184
7d9736ba
NBV
185 def has_delete_permission(self, request, obj=None):
186 user_groupes = request.user.groups.all()
26add0fd 187 if request.user.is_superuser is True or \
86e4bb65
DB
188 grp_drh in user_groupes or \
189 grp_drh2 in user_groupes or \
190 grp_directeurs_bureau in user_groupes or \
191 grp_administrateurs in user_groupes or \
192 grp_haute_direction in user_groupes:
7d9736ba 193 return True
b31ce2d7 194 return False
7d9736ba 195
f6724c20
NBV
196 def has_change_permission(self, request, obj=None):
197 user_groupes = request.user.groups.all()
26add0fd 198 if request.user.is_superuser is True or \
86e4bb65
DB
199 grp_drh in user_groupes or \
200 grp_drh2 in user_groupes or \
201 grp_directeurs_bureau in user_groupes or \
202 grp_administrateurs in user_groupes or \
203 grp_haute_direction in user_groupes:
f6724c20 204 return True
b31ce2d7
EMS
205 return False
206
f6724c20
NBV
207
208class ProxyOffreEmploiAdmin(OffreEmploiAdmin):
b31ce2d7
EMS
209 list_display = (
210 'nom', 'date_limite', 'region', 'statut', 'est_affiche'
211 )
212 readonly_fields = (
213 'description', 'bureau', 'duree_affectation', 'renumeration',
214 'debut_affectation', 'lieu_affectation', 'nom', 'resume',
215 'date_limite', 'region', 'poste'
216 )
f6724c20 217 fieldsets = (
720c3ad5 218 ('Nom', {
b31ce2d7 219 'fields': ('nom',)
f6724c20 220 }),
720c3ad5 221 ('Description générale', {
b31ce2d7 222 'fields': ('description', 'date_limite',)
f6724c20 223 }),
720c3ad5 224 ('Coordonnées', {
13389dae 225 'fields': ('lieu_affectation', 'bureau', 'region', 'poste',)
f6724c20 226 }),
720c3ad5 227 ('Autre', {
b31ce2d7
EMS
228 'fields': (
229 'debut_affectation', 'duree_affectation', 'renumeration',
230 )
f6724c20 231 }),
b31ce2d7 232 )
464e5825 233 inlines = []
7d9736ba 234
b31ce2d7 235 ### Lieu de redirection après le change
a029f641 236 def response_change(self, request, obj):
b31ce2d7 237 return redirect('admin:recrutement_proxyoffreemploi_changelist')
a029f641 238
be343ed2
NBV
239 ### Formulaire
240 def get_form(self, request, obj=None, **kwargs):
241 form = super(OffreEmploiAdmin, self).get_form(request, obj, **kwargs)
242 return form
243
7d9736ba 244 ### Permissions add, delete, change
f6724c20
NBV
245 def has_add_permission(self, request):
246 return False
247
248 def has_delete_permission(self, request, obj=None):
249 return False
250
2d083449 251 def has_change_permission(self, request, obj=None):
71aec2c3
OL
252 user_groupes = request.user.groups.all()
253 if request.user.is_superuser is True or \
86e4bb65
DB
254 grp_correspondants_rh in user_groupes or \
255 grp_drh in user_groupes or \
256 grp_drh2 in user_groupes or \
257 grp_directeurs_bureau in user_groupes or \
258 grp_administrateurs in user_groupes or \
259 grp_haute_direction in user_groupes:
71aec2c3 260 return True
46c508f5
OL
261
262 if obj is not None:
263 return True
264
71aec2c3 265 return False
2d083449 266
b31ce2d7 267
572c8d08
NBV
268class CandidatPieceInline(admin.TabularInline):
269 model = CandidatPiece
270 fields = ('candidat', 'nom', 'path',)
271 extra = 1
272 max_num = 3
273
b31ce2d7 274
c69ecb4f
OL
275class ReadOnlyCandidatPieceInline(CandidatPieceInline):
276 readonly_fields = ('candidat', 'nom', 'path', )
277 cand_delete = False
278
279
572c8d08
NBV
280class CandidatEvaluationInlineFormSet(BaseInlineFormSet):
281 """
282 Empêche la suppression d'une évaluation pour le CandidatEvaluationInline
283 """
284 def __init__(self, *args, **kwargs):
285 super(CandidatEvaluationInlineFormSet, self).__init__(*args, **kwargs)
b31ce2d7
EMS
286 self.can_delete = False
287
572c8d08
NBV
288
289class CandidatEvaluationInline(admin.TabularInline):
290 model = CandidatEvaluation
291 fields = ('evaluateur', 'note', 'commentaire')
292 max_num = 0
293 extra = 0
294 formset = CandidatEvaluationInlineFormSet
7d9736ba 295
b31ce2d7 296 ### Fields readonly
572c8d08
NBV
297 def get_readonly_fields(self, request, obj=None):
298 """
299 Empêche la modification des évaluations
300 """
301 if obj:
b31ce2d7 302 return self.readonly_fields + ('evaluateur', 'note', 'commentaire')
572c8d08
NBV
303 return self.readonly_fields
304
b31ce2d7 305
08a9b6fc 306class CandidatAdmin(BaseAdmin, VersionAdmin):
b31ce2d7 307 search_fields = ('nom', 'prenom')
7d0ae1ba 308 exclude = ('actif', )
940c9dd8 309 list_editable = ('statut', )
1b4c8020 310 list_display = ('_candidat', 'offre_emploi',
940c9dd8
OL
311 'voir_offre_emploi', 'calculer_moyenne',
312 'afficher_candidat', '_date_creation', 'statut', )
313 list_filter = ('offre_emploi', 'offre_emploi__region', 'statut', )
7d0ae1ba 314
7f9e891e 315 fieldsets = (
4896b661 316 ("Offre d'emploi", {
317 'fields': ('offre_emploi', )
318 }),
7f9e891e 319 ('Informations personnelles', {
b31ce2d7
EMS
320 'fields': (
321 'prenom', 'nom', 'genre', 'nationalite',
322 'situation_famille', 'nombre_dependant'
323 )
7f9e891e 324 }),
ec517164 325 ('Coordonnées', {
b31ce2d7
EMS
326 'fields': (
327 'telephone', 'email', 'adresse', 'ville', 'etat_province',
328 'code_postal', 'pays'
329 )
7f9e891e 330 }),
331 ('Informations professionnelles', {
b31ce2d7
EMS
332 'fields': (
333 'niveau_diplome', 'employeur_actuel', 'poste_actuel',
334 'domaine_professionnel'
335 )
336 }),
65c4cbd9
NBV
337 ('Traitement', {
338 'fields': ('statut', )
7f9e891e 339 }),
340 )
170c9aa2 341 inlines = [
342 CandidatPieceInline,
572c8d08 343 CandidatEvaluationInline,
170c9aa2 344 ]
0b9dea96 345 actions = ['envoyer_courriel_candidats', 'changer_statut']
a029f641 346
1b4c8020 347 def _candidat(self, obj):
b31ce2d7 348 txt = u"%s %s (%s)" % (obj.nom.upper(), obj.prenom, obj.genre)
1b4c8020
OL
349 txt = textwrap.wrap(txt, 30)
350 return "<br/>".join(txt)
351 _candidat.short_description = "Candidat"
352 _candidat.admin_order_field = "nom"
353 _candidat.allow_tags = True
354
940c9dd8
OL
355 def _date_creation(self, obj):
356 return obj.date_creation
1b4c8020
OL
357 _date_creation.admin_order_field = "date_creation"
358 _date_creation.short_description = "Date de réception"
940c9dd8 359
7d9736ba 360 ### Actions à afficher
a029f641
NBV
361 def get_actions(self, request):
362 actions = super(CandidatAdmin, self).get_actions(request)
363 del actions['delete_selected']
540dfae4 364 return actions
362a3534 365
7d9736ba 366 ### Envoyer un courriel à des candidats
b31ce2d7 367 def envoyer_courriel_candidats(modeladmin, obj, candidats):
52765380 368 selected = obj.POST.getlist(admin.ACTION_CHECKBOX_NAME)
369
b31ce2d7
EMS
370 return HttpResponseRedirect(
371 reverse('selectionner_template') + "?ids=%s" % (",".join(selected))
372 )
52765380 373 envoyer_courriel_candidats.short_description = u'Envoyer courriel'
374
0b9dea96
PP
375 ### Changer le statut à des candidats
376 def changer_statut(modeladmin, request, queryset):
377 if request.POST.get('post'):
378 queryset.update(statut=request.POST.get('statut'))
379 return None
380
381 context = {
382 'action_checkbox_name': admin.helpers.ACTION_CHECKBOX_NAME,
383 'queryset': queryset,
384 'status': STATUT_CHOICES,
385 }
386
387 return render_to_response("recrutement/selectionner_statut.html",
388 context, context_instance = RequestContext(request))
389
390 changer_statut.short_description = u'Changer statut'
391
7d9736ba 392 ### Évaluer un candidat
596fe324 393 def evaluer_candidat(self, obj):
b31ce2d7
EMS
394 return "<a href='%s?candidat__id__exact=%s'>" \
395 "Évaluer le candidat</a>" % (
396 reverse('admin:recrutement_candidatevaluation_changelist'),
397 obj.id
398 )
399 evaluer_candidat.allow_tags = True
beef7690 400 evaluer_candidat.short_description = 'Évaluation'
596fe324 401
7d9736ba 402 ### Afficher un candidat
7d82fd33 403 def afficher_candidat(self, obj):
d2cbe75d
OL
404 items = [u"<li><a href='%s%s'>%s</li>" % \
405 (settings.OE_PRIVE_MEDIA_URL, pj.path, pj.get_nom_display()) \
406 for pj in obj.pieces_jointes()]
b31ce2d7
EMS
407 html = "<a href='%s'>Voir le candidat</a>" % (
408 reverse('admin:recrutement_proxycandidat_change', args=(obj.id,))
409 )
d2cbe75d 410 return "%s<ul>%s</ul>" % (html, "\n".join(items))
b31ce2d7 411 afficher_candidat.allow_tags = True
8e0d552d 412 afficher_candidat.short_description = u'Détails du candidat'
7d82fd33 413
7d9736ba 414 ### Voir l'offre d'emploi
8941aee7 415 def voir_offre_emploi(self, obj):
b31ce2d7
EMS
416 return "<a href='%s'>Voir l'offre d'emploi</a>" % (reverse(
417 'admin:recrutement_proxyoffreemploi_change',
418 args=(obj.offre_emploi.id,)
419 ))
8941aee7 420 voir_offre_emploi.allow_tags = True
421 voir_offre_emploi.short_description = "Afficher l'offre d'emploi"
422
7d9736ba 423 ### Calculer la moyenne des notes
8941aee7 424 def calculer_moyenne(self, obj):
425 evaluations = CandidatEvaluation.objects.filter(candidat=obj)
8941aee7 426
940c9dd8 427 notes = [evaluation.note for evaluation in evaluations \
f6724c20 428 if evaluation.note is not None]
b31ce2d7 429
7d0ae1ba 430 if len(notes) > 0:
d6790a12 431 moyenne_votes = round(float(sum(notes)) / len(notes), 2)
8941aee7 432 else:
433 moyenne_votes = "Non disponible"
940c9dd8
OL
434
435 totales = len(evaluations)
436 faites = len(notes)
437
b31ce2d7 438 if obj.statut == 'REC':
940c9dd8
OL
439 if totales == faites:
440 color = "green"
441 elif faites > 0 and float(totales) / float(faites) >= 2:
442 color = "orange"
443 else:
444 color = "red"
445 else:
446 color = "black"
447
b31ce2d7
EMS
448 return """<span style="color: %s;">%s (%s/%s)</span>""" % (
449 color, moyenne_votes, faites, totales
450 )
8941aee7 451 calculer_moyenne.allow_tags = True
3eb0be91 452 calculer_moyenne.short_description = "Moyenne"
d6790a12 453 calculer_moyenne.admin_order_field = ""
8941aee7 454
7d9736ba 455 ### Permissions add, delete, change
3a542b85 456 def has_add_permission(self, request):
f6724c20 457 user_groupes = request.user.groups.all()
26add0fd 458 if request.user.is_superuser is True or \
86e4bb65
DB
459 grp_correspondants_rh in user_groupes or \
460 grp_drh in user_groupes or \
461 grp_drh2 in user_groupes or \
462 grp_directeurs_bureau in user_groupes or \
463 grp_administrateurs in user_groupes or \
464 grp_haute_direction in user_groupes:
f6724c20 465 return True
b31ce2d7 466 return False
4896b661 467
f6724c20 468 def has_delete_permission(self, request, obj=None):
3a542b85 469 user_groupes = request.user.groups.all()
26add0fd 470 if request.user.is_superuser is True or \
86e4bb65
DB
471 grp_correspondants_rh in user_groupes or \
472 grp_drh in user_groupes or \
473 grp_drh2 in user_groupes or \
474 grp_directeurs_bureau in user_groupes or \
475 grp_administrateurs in user_groupes or \
476 grp_haute_direction in user_groupes:
3a542b85 477 return True
b31ce2d7 478 return False
f6724c20
NBV
479
480 def has_change_permission(self, request, obj=None):
481 user_groupes = request.user.groups.all()
26add0fd 482 if request.user.is_superuser is True or \
86e4bb65
DB
483 grp_correspondants_rh in user_groupes or \
484 grp_drh in user_groupes or \
485 grp_drh2 in user_groupes or \
486 grp_directeurs_bureau in user_groupes or \
487 grp_administrateurs in user_groupes or \
488 grp_haute_direction in user_groupes:
f6724c20 489 return True
97b168d6
OL
490 return False
491
492 def get_changelist(self, request, **kwargs):
493 return OrderedChangeList
4896b661 494
d46075cb 495 def queryset(self, request):
f9983b5a 496 """
b31ce2d7
EMS
497 Spécifie un queryset limité, autrement Django exécute un
498 select_related() sans paramètre, ce qui a pour effet de charger tous
499 les objets FK, sans limite de profondeur. Dès qu'on arrive, dans les
500 modèles de Region, il existe plusieurs boucles, ce qui conduit à la
8ea41642 501 génération d'une requête infinie.
f9983b5a 502 """
b31ce2d7
EMS
503 qs = self.model._default_manager.get_query_set() \
504 .select_related('offre_emploi') \
505 .annotate(moyenne=Avg('evaluations__note'))
97b168d6 506
d46075cb 507 user_groupes = request.user.groups.all()
86e4bb65
DB
508 if grp_drh in user_groupes or \
509 grp_drh2 in user_groupes or \
510 grp_haute_direction in user_groupes:
d6790a12 511 return qs
7d9736ba 512
86e4bb65
DB
513 if grp_directeurs_bureau in user_groupes or \
514 grp_correspondants_rh in user_groupes or \
515 grp_administrateurs in user_groupes:
7d9736ba 516 employe = get_emp(request.user)
d6790a12 517 return qs.filter(offre_emploi__region=employe.implantation.region)
7d9736ba 518
c69ecb4f 519 if Evaluateur.objects.filter(user=request.user).exists():
940c9dd8
OL
520 evaluateur = Evaluateur.objects.get(user=request.user)
521 candidat_ids = [e.candidat.id for e in
522 CandidatEvaluation.objects.filter(evaluateur=evaluateur)]
d6790a12 523 return qs.filter(id__in=candidat_ids)
b31ce2d7 524 return qs.none()
f6724c20 525
613e8dfe 526
f6724c20 527class ProxyCandidatAdmin(CandidatAdmin):
940c9dd8 528 list_editable = ()
b31ce2d7
EMS
529 readonly_fields = (
530 'statut', 'offre_emploi', 'prenom', 'nom', 'genre', 'nationalite',
531 'situation_famille', 'nombre_dependant', 'telephone', 'email',
532 'adresse', 'ville', 'etat_province', 'code_postal', 'pays',
533 'niveau_diplome', 'employeur_actuel', 'poste_actuel',
534 'domaine_professionnel', 'pieces_jointes'
535 )
2d083449
NBV
536 fieldsets = (
537 ("Offre d'emploi", {
538 'fields': ('offre_emploi', )
539 }),
540 ('Informations personnelles', {
b31ce2d7
EMS
541 'fields': (
542 'prenom', 'nom', 'genre', 'nationalite', 'situation_famille',
543 'nombre_dependant'
544 )
2d083449
NBV
545 }),
546 ('Coordonnées', {
b31ce2d7
EMS
547 'fields': (
548 'telephone', 'email', 'adresse', 'ville', 'etat_province',
549 'code_postal', 'pays'
550 )
2d083449
NBV
551 }),
552 ('Informations professionnelles', {
b31ce2d7
EMS
553 'fields': (
554 'niveau_diplome', 'employeur_actuel', 'poste_actuel',
555 'domaine_professionnel'
556 )
557 }),
2d083449 558 )
c69ecb4f 559 inlines = (CandidatEvaluationInline, )
2d083449 560
f6724c20
NBV
561 def has_add_permission(self, request):
562 return False
563
564 def has_delete_permission(self, request, obj=None):
565 return False
2adf9e0c 566
2d083449 567 def has_change_permission(self, request, obj=None):
71aec2c3
OL
568 user_groupes = request.user.groups.all()
569 if request.user.is_superuser is True or \
86e4bb65
DB
570 grp_correspondants_rh in user_groupes or \
571 grp_drh in user_groupes or \
572 grp_drh2 in user_groupes or \
573 grp_directeurs_bureau in user_groupes or \
574 grp_administrateurs in user_groupes or \
575 grp_haute_direction in user_groupes:
71aec2c3 576 return True
b44ee2a0
OL
577
578 if obj is not None:
579 evaluateur = Evaluateur.objects.get(user=request.user)
580 for e in obj.evaluations.all():
581 if e.evaluateur == evaluateur:
582 return True
583
71aec2c3 584 return False
2d083449 585
fe6fe6bb
OL
586 def get_actions(self, request):
587 return None
588
b31ce2d7 589
2e9ee615 590class CandidatPieceAdmin(admin.ModelAdmin):
170c9aa2 591 list_display = ('nom', 'candidat', )
592
7d9736ba 593 ### Queryset
170c9aa2 594 def queryset(self, request):
595 """
b31ce2d7
EMS
596 Spécifie un queryset limité, autrement Django exécute un
597 select_related() sans paramètre, ce qui a pour effet de charger tous
598 les objets FK, sans limite de profondeur. Dès qu'on arrive, dans les
599 modèles de Region, il existe plusieurs boucles, ce qui conduit à la
600 génération d'une requête infinie. Affiche la liste de candidats que
601 si le user connecté possède un Evaluateur
170c9aa2 602 """
603 qs = self.model._default_manager.get_query_set()
604 return qs.select_related('candidat')
2e9ee615 605
b31ce2d7 606
08a9b6fc 607class EvaluateurAdmin(BaseAdmin, VersionAdmin):
eb579d40 608 fieldsets = (
540dfae4
NBV
609 ("Utilisateur", {
610 'fields': ('user',)
611 }),
eb579d40 612 )
4418c732 613
7d9736ba 614 ### Actions à afficher
a029f641
NBV
615 def get_actions(self, request):
616 actions = super(EvaluateurAdmin, self).get_actions(request)
617 del actions['delete_selected']
540dfae4 618 return actions
a029f641 619
7d9736ba
NBV
620 ### Permissions add, delete, change
621 def has_add_permission(self, request):
622 user_groupes = request.user.groups.all()
26add0fd 623 if request.user.is_superuser is True or \
86e4bb65
DB
624 grp_drh in user_groupes or \
625 grp_drh2 in user_groupes or \
626 grp_haute_direction in user_groupes:
7d9736ba 627 return True
b31ce2d7 628 return False
7d9736ba
NBV
629
630 def has_delete_permission(self, request, obj=None):
631 user_groupes = request.user.groups.all()
26add0fd 632 if request.user.is_superuser is True or \
86e4bb65
DB
633 grp_drh in user_groupes or \
634 grp_drh2 in user_groupes or \
635 grp_haute_direction in user_groupes:
7d9736ba 636 return True
b31ce2d7 637 return False
7d9736ba
NBV
638
639 def has_change_permission(self, request, obj=None):
640 user_groupes = request.user.groups.all()
26add0fd 641 if request.user.is_superuser is True or \
86e4bb65
DB
642 grp_drh in user_groupes or \
643 grp_drh2 in user_groupes or \
644 grp_haute_direction in user_groupes:
7d9736ba 645 return True
b31ce2d7
EMS
646 return False
647
27c81d11 648
08a9b6fc 649class CandidatEvaluationAdmin(BaseAdmin):
b31ce2d7
EMS
650 search_fields = ('candidat__nom', 'candidat__prenom')
651 list_display = (
652 '_candidat', '_statut', '_offre_emploi', 'evaluateur', '_note',
653 '_commentaire'
654 )
940c9dd8
OL
655 readonly_fields = ('candidat', 'evaluateur')
656 list_filter = ('candidat__statut', 'candidat__offre_emploi',)
beef7690
NBV
657 fieldsets = (
658 ('Évaluation du candidat', {
b31ce2d7 659 'fields': ('candidat', 'evaluateur', 'note', 'commentaire', )
beef7690
NBV
660 }),
661 )
662
a029f641 663 def get_actions(self, request):
5fe6986f
OL
664 # on stocke l'evaluateur connecté (pas forcément la meilleure place...)
665 try:
666 self.evaluateur = Evaluateur.objects.get(user=request.user)
667 except:
668 self.evaluateur = None
669
a029f641
NBV
670 actions = super(CandidatEvaluationAdmin, self).get_actions(request)
671 del actions['delete_selected']
540dfae4 672 return actions
a029f641 673
7d9736ba 674 ### Afficher la note
beef7690
NBV
675 def _note(self, obj):
676 """
677 Si l'évaluateur n'a pas encore donné de note au candidat, indiquer
678 un lien pour Évaluer le candidat.
679 Sinon afficher la note.
680 """
c44cb3de
OL
681 page = self.model.__name__.lower()
682 redirect_url = 'admin:recrutement_%s_change' % page
b31ce2d7 683
beef7690 684 if obj.note is None:
5fe6986f
OL
685 label = "Candidat non évalué"
686 else:
687 label = obj.note
688
689 if self.evaluateur == obj.evaluateur:
b31ce2d7
EMS
690 return "<a href='%s'>%s</a>" % (
691 reverse(redirect_url, args=(obj.id,)), label
692 )
5fe6986f
OL
693 else:
694 return label
beef7690 695 _note.allow_tags = True
b31ce2d7
EMS
696 _note.short_description = "Note"
697 _note.admin_order_field = 'note'
beef7690 698
940c9dd8
OL
699 def _statut(self, obj):
700 return obj.candidat.get_statut_display()
701 _statut.order_field = 'candidat__statut'
702 _statut.short_description = 'Statut'
703
7d9736ba 704 ### Lien en lecture seule vers le candidat
beef7690
NBV
705 def _candidat(self, obj):
706 return "<a href='%s'>%s</a>" \
b31ce2d7 707 % (reverse('admin:recrutement_proxycandidat_change',
beef7690 708 args=(obj.candidat.id,)), obj.candidat)
b31ce2d7 709 _candidat.allow_tags = True
beef7690
NBV
710 _candidat.short_description = 'Candidat'
711
7d9736ba 712 ### Afficher commentaire
beef7690
NBV
713 def _commentaire(self, obj):
714 """
715 Si l'évaluateur n'a pas encore donné de note au candidat, indiquer
716 dans le champ commentaire, Aucun au lieu de (None)
717 Sinon afficher la note.
718 """
719 if obj.commentaire is None:
720 return "Aucun"
721 return obj.commentaire
722 _commentaire.allow_tags = True
b31ce2d7 723 _commentaire.short_description = "Commentaire"
720c3ad5 724
7d9736ba 725 ### Afficher offre d'emploi
beef7690
NBV
726 def _offre_emploi(self, obj):
727 return "<a href='%s'>%s</a>" % \
b31ce2d7 728 (reverse('admin:recrutement_proxyoffreemploi_change',
dc7faf2b 729 args=(obj.candidat.offre_emploi.id,)), obj.candidat.offre_emploi)
beef7690
NBV
730 _offre_emploi.allow_tags = True
731 _offre_emploi.short_description = "Voir offre d'emploi"
b31ce2d7 732
7d9736ba 733 def has_add_permission(self, request):
940c9dd8
OL
734 return False
735
736 def has_delete_permission(self, request, obj=None):
737 return False
7d9736ba 738
21b02da5
NBV
739 def has_change_permission(self, request, obj=None):
740 """
741 Permettre la visualisation dans la changelist
742 mais interdire l'accès à modifier l'objet si l'évaluateur n'est pas
743 le request.user
744 """
f133459f
OL
745 user_groupes = request.user.groups.all()
746
3bc5ea42 747 if request.user.is_superuser or \
86e4bb65
DB
748 grp_drh in user_groupes or \
749 grp_drh2 in user_groupes or \
750 grp_correspondants_rh in user_groupes or \
751 grp_directeurs_bureau in user_groupes or \
752 grp_administrateurs in user_groupes or \
753 grp_haute_direction in user_groupes:
c5a263d6 754 is_recrutement = True
f133459f 755 else:
6c6c51bb 756 is_recrutement = False
f133459f 757
f6996aa3 758 return is_recrutement
f133459f 759
720c3ad5 760 def queryset(self, request):
beef7690 761 """
b31ce2d7 762 Afficher uniquement les évaluations de l'évaluateur, sauf si
7d9736ba 763 l'utilisateur est dans les groupes suivants.
beef7690 764 """
b31ce2d7
EMS
765 qs = self.model._default_manager.get_query_set() \
766 .select_related('offre_emploi')
beef7690 767 user_groupes = request.user.groups.all()
940c9dd8 768
86e4bb65
DB
769 if grp_drh in user_groupes or \
770 grp_drh2 in user_groupes or \
771 grp_correspondants_rh in user_groupes or \
772 grp_directeurs_bureau in user_groupes or \
773 grp_administrateurs in user_groupes or \
774 grp_haute_direction in user_groupes:
940c9dd8
OL
775 return qs
776
b31ce2d7 777 evaluateur = Evaluateur.objects.get(user=request.user)
940c9dd8
OL
778 candidats_evaluations = \
779 CandidatEvaluation.objects.filter(evaluateur=evaluateur,
780 candidat__statut__in=('REC', ))
781 candidats_evaluations_ids = [ce.id for ce in candidats_evaluations]
782 return qs.filter(id__in=candidats_evaluations_ids)
596fe324 783
3bc5ea42
OL
784
785class MesCandidatEvaluationAdmin(CandidatEvaluationAdmin):
786
787 def has_change_permission(self, request, obj=None):
788 try:
789 Evaluateur.objects.get(user=request.user)
790 is_evaluateur = True
791 except:
792 is_evaluateur = False
793
5fe6986f 794 if obj is None and is_evaluateur:
3bc5ea42
OL
795 return True
796
797 try:
798 return request.user == obj.evaluateur.user
799 except:
800 return False
801
802 def queryset(self, request):
b31ce2d7
EMS
803 qs = self.model._default_manager.get_query_set() \
804 .select_related('offre_emploi')
805 evaluateur = Evaluateur.objects.get(user=request.user)
3bc5ea42
OL
806 candidats_evaluations = \
807 CandidatEvaluation.objects.filter(evaluateur=evaluateur,
808 candidat__statut__in=('REC', ))
809 candidats_evaluations_ids = [ce.id for ce in candidats_evaluations]
810 return qs.filter(id__in=candidats_evaluations_ids)
811
812
08a9b6fc 813class CourrielTemplateAdmin(BaseAdmin, VersionAdmin):
7d9736ba 814 ### Actions à afficher
a029f641
NBV
815 def get_actions(self, request):
816 actions = super(CourrielTemplateAdmin, self).get_actions(request)
817 del actions['delete_selected']
540dfae4 818 return actions
4e8340cf 819
df59fcab 820admin.site.register(OffreEmploi, OffreEmploiAdmin)
382501c1 821admin.site.register(ProxyOffreEmploi, ProxyOffreEmploiAdmin)
df59fcab 822admin.site.register(Candidat, CandidatAdmin)
382501c1 823admin.site.register(ProxyCandidat, ProxyCandidatAdmin)
720c3ad5 824admin.site.register(CandidatEvaluation, CandidatEvaluationAdmin)
3bc5ea42 825admin.site.register(MesCandidatEvaluation, MesCandidatEvaluationAdmin)
27c81d11 826admin.site.register(Evaluateur, EvaluateurAdmin)
32834000 827admin.site.register(CourrielTemplate, CourrielTemplateAdmin)