test offre emploi add
[auf_rh_dae.git] / project / recrutement / admin.py
CommitLineData
df59fcab 1# -*- encoding: utf-8 -*-
2
1b4c8020 3import textwrap
4ba84959 4
d104b0ae
EMS
5from auf.django.emploi.models import OffreEmploi, Candidat, CandidatPiece
6from auf.django.references.models import Region, Bureau
75f0e87b 7from django.conf import settings
df59fcab 8from django.contrib import admin
75f0e87b 9from django.core.urlresolvers import reverse
97b168d6 10from django.db.models import Avg
75f0e87b
DB
11from django.forms.models import BaseInlineFormSet
12from django.http import HttpResponseRedirect
b31ce2d7 13from django.shortcuts import redirect
6067184b 14from reversion.admin import VersionAdmin
7d9736ba 15
3383b2d1 16from project import groups
17c90428 17from project.rh import models as rh
b31ce2d7
EMS
18from project.recrutement.forms import OffreEmploiForm
19from project.recrutement.models import \
4ba84959
EMS
20 Evaluateur, CandidatEvaluation, \
21 ProxyOffreEmploi, ProxyCandidat, MesCandidatEvaluation, \
b31ce2d7 22 CourrielTemplate
df59fcab 23
7d9736ba 24### CONSTANTES
d333c690
NBV
25IMPLANTATIONS_CENTRALES = [15, 19]
26
b31ce2d7 27
08a9b6fc
EMS
28class BaseAdmin(admin.ModelAdmin):
29
30 class Media:
dd6c0df2
EMS
31 css = {'screen': (
32 'css/admin_custom.css',
33 'jquery-autocomplete/jquery.autocomplete.css',
34 )}
35 js = (
36 'https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js',
37 'jquery-autocomplete/jquery.autocomplete.min.js',
38 )
08a9b6fc
EMS
39
40
97b168d6
OL
41class OrderedChangeList(admin.views.main.ChangeList):
42 """
43 Surcharge pour appliquer le order_by d'un annotate
44 """
45 def get_query_set(self):
46 qs = super(OrderedChangeList, self).get_query_set()
47 qs = qs.order_by('-moyenne')
48 return qs
49
b31ce2d7 50
d104b0ae 51class OffreEmploiAdminMixin(BaseAdmin):
7f9e891e 52 date_hierarchy = 'date_creation'
b31ce2d7
EMS
53 list_display = (
54 'nom', 'date_limite', 'region', 'statut', 'est_affiche',
55 '_candidatsList'
56 )
7d0ae1ba
NBV
57 exclude = ('actif', 'poste_nom', 'resume',)
58 list_filter = ('statut',)
c4874d66 59 actions = ['affecter_evaluateurs_offre_emploi', ]
a084e988 60 form = OffreEmploiForm
2c3c54ee 61
7d9736ba 62 ### Actions à afficher
a029f641 63 def get_actions(self, request):
62201d27 64 actions = super(OffreEmploiAdminMixin, self).get_actions(request)
a029f641 65 del actions['delete_selected']
540dfae4 66 return actions
a029f641 67
7d9736ba 68 ### Affecter un évaluateurs à des offres d'emploi
b31ce2d7 69 def affecter_evaluateurs_offre_emploi(modeladmin, obj, candidats):
c4874d66
NBV
70 selected = obj.POST.getlist(admin.ACTION_CHECKBOX_NAME)
71
b31ce2d7
EMS
72 return HttpResponseRedirect(
73 reverse('affecter_evaluateurs_offre_emploi') +
74 "?ids=%s" % (",".join(selected))
75 )
76
77 affecter_evaluateurs_offre_emploi.short_description = \
78 u'Affecter évaluateur(s)'
c4874d66 79
7d9736ba 80 ### Afficher la liste des candidats pour l'offre d'emploi
b31ce2d7 81 def _candidatsList(self, obj):
8ea41642 82 return "<a href='%s?offre_emploi__id__exact=%s'>Voir les candidats \
62201d27 83 </a>" % (reverse('admin:recrutement_proxycandidat_changelist'), obj.id)
b31ce2d7 84 _candidatsList.allow_tags = True
f6724c20 85 _candidatsList.short_description = "Afficher la liste des candidats"
362a3534 86
7d9736ba 87 ### Formulaire
3a542b85 88 def get_form(self, request, obj=None, **kwargs):
9224a467 89 form = super(OffreEmploiAdminMixin, self).get_form(request, obj, **kwargs)
3383b2d1
OL
90 employe = groups.get_employe_from_user(request.user)
91 user_groupes = [g.name for g in request.user.groups.all()]
b31ce2d7 92
7d9736ba 93 # Region
29b32b82 94
b31ce2d7 95 if 'region' in form.declared_fields:
d333c690 96 region_field = form.declared_fields['region']
29b32b82
OL
97 read_only = False
98 elif 'region' in form.base_fields:
d333c690 99 region_field = form.base_fields['region']
29b32b82 100 read_only = False
d333c690 101 else:
29b32b82
OL
102 read_only = True
103
104 if not read_only:
105 if groups.DRH_NIVEAU_1 in user_groupes or \
106 groups.DRH_NIVEAU_2 in user_groupes or \
107 groups.HAUTE_DIRECTION in user_groupes:
108 region_field.queryset = Region.objects.all()
109 else:
110 region_field.queryset = Region.objects.\
d333c690 111 filter(id=employe.implantation.region.id)
b31ce2d7 112
7d9736ba 113 # Poste
b31ce2d7 114 if 'poste' in form.declared_fields:
d333c690 115 poste_field = form.declared_fields['poste']
29b32b82
OL
116 read_only = False
117 elif 'poste' in form.base_fields:
d333c690 118 poste_field = form.base_fields['poste']
29b32b82 119 read_only = False
d333c690 120 else:
29b32b82
OL
121 read_only = True
122
123 if not read_only:
124 if groups.DRH_NIVEAU_1 in user_groupes or \
125 groups.DRH_NIVEAU_2 in user_groupes or \
126 groups.HAUTE_DIRECTION in user_groupes:
127 poste_field.queryset = rh.Poste.objects.all()
128 else:
129 poste_field.queryset = rh.Poste.objects.\
130 filter(implantation__region=employe.implantation.region).\
131 exclude(implantation__in=IMPLANTATIONS_CENTRALES)
b31ce2d7 132
7d9736ba 133 # Bureau
b31ce2d7 134 if 'bureau' in form.declared_fields:
d333c690 135 bureau_field = form.declared_fields['bureau']
29b32b82
OL
136 read_only = False
137 elif 'bureau' in form.base_fields:
d333c690 138 bureau_field = form.base_fields['bureau']
29b32b82 139 read_only = False
d333c690 140 else:
29b32b82
OL
141 read_only = True
142 if not read_only:
143 if groups.DRH_NIVEAU_1 in user_groupes or \
144 groups.DRH_NIVEAU_2 in user_groupes or \
145 groups.HAUTE_DIRECTION in user_groupes:
146 bureau_field.queryset = Bureau.objects.all()
147 else:
148 bureau_field.queryset = \
149 Bureau.objects.filter(region=employe.implantation.region)
b31ce2d7 150
3a542b85 151 return form
b31ce2d7 152
7d9736ba 153 ### Queryset
b31ce2d7 154
2f78949d 155 def queryset(self, request):
b31ce2d7
EMS
156 qs = self.model._default_manager.get_query_set() \
157 .select_related('offre_emploi')
62201d27 158 user_groupes = [g.name for g in request.user.groups.all()]
3383b2d1
OL
159 if groups.DRH_NIVEAU_1 in user_groupes or \
160 groups.DRH_NIVEAU_2 in user_groupes or \
161 groups.HAUTE_DIRECTION in user_groupes:
940c9dd8 162 return qs
f6724c20 163
3383b2d1
OL
164 if groups.DIRECTEUR_DE_BUREAU in user_groupes or \
165 groups.CORRESPONDANT_RH in user_groupes or \
166 groups.ADMINISTRATEURS in user_groupes:
167 employe = groups.get_employe_from_user(request.user)
940c9dd8 168 return qs.filter(region=employe.implantation.region)
7d9736ba 169
c69ecb4f 170 if Evaluateur.objects.filter(user=request.user).exists():
940c9dd8 171 evaluateur = Evaluateur.objects.get(user=request.user)
b31ce2d7
EMS
172 offre_ids = [
173 e.candidat.offre_emploi_id
174 for e in CandidatEvaluation.objects
175 .select_related('candidat')
176 .filter(evaluateur=evaluateur)
177 ]
940c9dd8
OL
178 return qs.filter(id__in=offre_ids)
179
7d9736ba
NBV
180 return qs.none()
181
182 ### Permission add, delete, change
3a542b85 183 def has_add_permission(self, request):
3383b2d1 184 user_groupes = [g.name for g in request.user.groups.all()]
26add0fd 185 if request.user.is_superuser is True or \
3383b2d1
OL
186 groups.DRH_NIVEAU_1 in user_groupes or \
187 groups.DRH_NIVEAU_2 in user_groupes or \
188 groups.DIRECTEUR_DE_BUREAU in user_groupes or \
189 groups.ADMINISTRATEURS in user_groupes or \
190 groups.HAUTE_DIRECTION in user_groupes:
3a542b85 191 return True
b31ce2d7 192 return False
3a542b85 193
7d9736ba 194 def has_delete_permission(self, request, obj=None):
3383b2d1 195 user_groupes = [g.name for g in request.user.groups.all()]
26add0fd 196 if request.user.is_superuser is True or \
3383b2d1
OL
197 groups.DRH_NIVEAU_1 in user_groupes or \
198 groups.DRH_NIVEAU_2 in user_groupes or \
199 groups.DIRECTEUR_DE_BUREAU in user_groupes or \
200 groups.ADMINISTRATEURS in user_groupes or \
201 groups.HAUTE_DIRECTION in user_groupes:
7d9736ba 202 return True
b31ce2d7 203 return False
7d9736ba 204
f6724c20 205 def has_change_permission(self, request, obj=None):
3383b2d1 206 user_groupes = [g.name for g in request.user.groups.all()]
26add0fd 207 if request.user.is_superuser is True or \
3383b2d1
OL
208 groups.DRH_NIVEAU_1 in user_groupes or \
209 groups.DRH_NIVEAU_2 in user_groupes or \
210 groups.DIRECTEUR_DE_BUREAU in user_groupes or \
211 groups.ADMINISTRATEURS in user_groupes or \
212 groups.HAUTE_DIRECTION in user_groupes:
f6724c20 213 return True
b31ce2d7
EMS
214 return False
215
f6724c20 216
d104b0ae
EMS
217class OffreEmploiAdmin(VersionAdmin, OffreEmploiAdminMixin):
218 pass
219
220
221class ProxyOffreEmploiAdmin(OffreEmploiAdminMixin):
b31ce2d7
EMS
222 list_display = (
223 'nom', 'date_limite', 'region', 'statut', 'est_affiche'
224 )
225 readonly_fields = (
226 'description', 'bureau', 'duree_affectation', 'renumeration',
227 'debut_affectation', 'lieu_affectation', 'nom', 'resume',
228 'date_limite', 'region', 'poste'
229 )
f6724c20 230 fieldsets = (
720c3ad5 231 ('Nom', {
b31ce2d7 232 'fields': ('nom',)
f6724c20 233 }),
720c3ad5 234 ('Description générale', {
b31ce2d7 235 'fields': ('description', 'date_limite',)
f6724c20 236 }),
720c3ad5 237 ('Coordonnées', {
13389dae 238 'fields': ('lieu_affectation', 'bureau', 'region', 'poste',)
f6724c20 239 }),
720c3ad5 240 ('Autre', {
b31ce2d7
EMS
241 'fields': (
242 'debut_affectation', 'duree_affectation', 'renumeration',
243 )
f6724c20 244 }),
b31ce2d7 245 )
464e5825 246 inlines = []
7d9736ba 247
b31ce2d7 248 ### Lieu de redirection après le change
a029f641 249 def response_change(self, request, obj):
b31ce2d7 250 return redirect('admin:recrutement_proxyoffreemploi_changelist')
a029f641 251
be343ed2
NBV
252 ### Formulaire
253 def get_form(self, request, obj=None, **kwargs):
29b32b82 254 form = super(ProxyOffreEmploiAdmin, self).get_form(request, obj, **kwargs)
be343ed2
NBV
255 return form
256
7d9736ba 257 ### Permissions add, delete, change
f6724c20
NBV
258 def has_add_permission(self, request):
259 return False
260
261 def has_delete_permission(self, request, obj=None):
262 return False
263
2d083449 264 def has_change_permission(self, request, obj=None):
3383b2d1 265 user_groupes = [g.name for g in request.user.groups.all()]
71aec2c3 266 if request.user.is_superuser is True or \
3383b2d1
OL
267 groups.CORRESPONDANT_RH in user_groupes or \
268 groups.DRH_NIVEAU_1 in user_groupes or \
269 groups.DRH_NIVEAU_2 in user_groupes or \
270 groups.DIRECTEUR_DE_BUREAU in user_groupes or \
271 groups.ADMINISTRATEURS in user_groupes or \
272 groups.HAUTE_DIRECTION in user_groupes:
71aec2c3 273 return True
46c508f5
OL
274
275 if obj is not None:
276 return True
277
71aec2c3 278 return False
2d083449 279
b31ce2d7 280
572c8d08
NBV
281class CandidatPieceInline(admin.TabularInline):
282 model = CandidatPiece
283 fields = ('candidat', 'nom', 'path',)
284 extra = 1
285 max_num = 3
286
b31ce2d7 287
c69ecb4f
OL
288class ReadOnlyCandidatPieceInline(CandidatPieceInline):
289 readonly_fields = ('candidat', 'nom', 'path', )
290 cand_delete = False
291
292
572c8d08
NBV
293class CandidatEvaluationInlineFormSet(BaseInlineFormSet):
294 """
295 Empêche la suppression d'une évaluation pour le CandidatEvaluationInline
296 """
297 def __init__(self, *args, **kwargs):
298 super(CandidatEvaluationInlineFormSet, self).__init__(*args, **kwargs)
b31ce2d7
EMS
299 self.can_delete = False
300
572c8d08
NBV
301
302class CandidatEvaluationInline(admin.TabularInline):
303 model = CandidatEvaluation
304 fields = ('evaluateur', 'note', 'commentaire')
305 max_num = 0
306 extra = 0
307 formset = CandidatEvaluationInlineFormSet
7d9736ba 308
b31ce2d7 309 ### Fields readonly
572c8d08
NBV
310 def get_readonly_fields(self, request, obj=None):
311 """
312 Empêche la modification des évaluations
313 """
314 if obj:
b31ce2d7 315 return self.readonly_fields + ('evaluateur', 'note', 'commentaire')
572c8d08
NBV
316 return self.readonly_fields
317
b31ce2d7 318
d104b0ae 319class CandidatAdminMixin(BaseAdmin):
b31ce2d7 320 search_fields = ('nom', 'prenom')
7d0ae1ba 321 exclude = ('actif', )
940c9dd8 322 list_editable = ('statut', )
1b4c8020 323 list_display = ('_candidat', 'offre_emploi',
940c9dd8
OL
324 'voir_offre_emploi', 'calculer_moyenne',
325 'afficher_candidat', '_date_creation', 'statut', )
326 list_filter = ('offre_emploi', 'offre_emploi__region', 'statut', )
7d0ae1ba 327
7f9e891e 328 fieldsets = (
4896b661 329 ("Offre d'emploi", {
330 'fields': ('offre_emploi', )
331 }),
7f9e891e 332 ('Informations personnelles', {
b31ce2d7
EMS
333 'fields': (
334 'prenom', 'nom', 'genre', 'nationalite',
335 'situation_famille', 'nombre_dependant'
336 )
7f9e891e 337 }),
ec517164 338 ('Coordonnées', {
b31ce2d7
EMS
339 'fields': (
340 'telephone', 'email', 'adresse', 'ville', 'etat_province',
341 'code_postal', 'pays'
342 )
7f9e891e 343 }),
344 ('Informations professionnelles', {
b31ce2d7
EMS
345 'fields': (
346 'niveau_diplome', 'employeur_actuel', 'poste_actuel',
347 'domaine_professionnel'
348 )
349 }),
65c4cbd9
NBV
350 ('Traitement', {
351 'fields': ('statut', )
7f9e891e 352 }),
353 )
170c9aa2 354 inlines = [
355 CandidatPieceInline,
572c8d08 356 CandidatEvaluationInline,
170c9aa2 357 ]
540dfae4 358 actions = ['envoyer_courriel_candidats']
a029f641 359
1b4c8020 360 def _candidat(self, obj):
b31ce2d7 361 txt = u"%s %s (%s)" % (obj.nom.upper(), obj.prenom, obj.genre)
1b4c8020
OL
362 txt = textwrap.wrap(txt, 30)
363 return "<br/>".join(txt)
364 _candidat.short_description = "Candidat"
365 _candidat.admin_order_field = "nom"
366 _candidat.allow_tags = True
367
940c9dd8
OL
368 def _date_creation(self, obj):
369 return obj.date_creation
1b4c8020
OL
370 _date_creation.admin_order_field = "date_creation"
371 _date_creation.short_description = "Date de réception"
940c9dd8 372
7d9736ba 373 ### Actions à afficher
a029f641
NBV
374 def get_actions(self, request):
375 actions = super(CandidatAdmin, self).get_actions(request)
376 del actions['delete_selected']
540dfae4 377 return actions
362a3534 378
7d9736ba 379 ### Envoyer un courriel à des candidats
b31ce2d7 380 def envoyer_courriel_candidats(modeladmin, obj, candidats):
52765380 381 selected = obj.POST.getlist(admin.ACTION_CHECKBOX_NAME)
382
b31ce2d7
EMS
383 return HttpResponseRedirect(
384 reverse('selectionner_template') + "?ids=%s" % (",".join(selected))
385 )
52765380 386 envoyer_courriel_candidats.short_description = u'Envoyer courriel'
387
7d9736ba 388 ### Évaluer un candidat
596fe324 389 def evaluer_candidat(self, obj):
b31ce2d7
EMS
390 return "<a href='%s?candidat__id__exact=%s'>" \
391 "Évaluer le candidat</a>" % (
392 reverse('admin:recrutement_candidatevaluation_changelist'),
393 obj.id
394 )
395 evaluer_candidat.allow_tags = True
beef7690 396 evaluer_candidat.short_description = 'Évaluation'
596fe324 397
7d9736ba 398 ### Afficher un candidat
7d82fd33 399 def afficher_candidat(self, obj):
d2cbe75d
OL
400 items = [u"<li><a href='%s%s'>%s</li>" % \
401 (settings.OE_PRIVE_MEDIA_URL, pj.path, pj.get_nom_display()) \
402 for pj in obj.pieces_jointes()]
b31ce2d7
EMS
403 html = "<a href='%s'>Voir le candidat</a>" % (
404 reverse('admin:recrutement_proxycandidat_change', args=(obj.id,))
405 )
d2cbe75d 406 return "%s<ul>%s</ul>" % (html, "\n".join(items))
b31ce2d7 407 afficher_candidat.allow_tags = True
8e0d552d 408 afficher_candidat.short_description = u'Détails du candidat'
7d82fd33 409
7d9736ba 410 ### Voir l'offre d'emploi
8941aee7 411 def voir_offre_emploi(self, obj):
b31ce2d7
EMS
412 return "<a href='%s'>Voir l'offre d'emploi</a>" % (reverse(
413 'admin:recrutement_proxyoffreemploi_change',
414 args=(obj.offre_emploi.id,)
415 ))
8941aee7 416 voir_offre_emploi.allow_tags = True
417 voir_offre_emploi.short_description = "Afficher l'offre d'emploi"
418
7d9736ba 419 ### Calculer la moyenne des notes
8941aee7 420 def calculer_moyenne(self, obj):
421 evaluations = CandidatEvaluation.objects.filter(candidat=obj)
8941aee7 422
940c9dd8 423 notes = [evaluation.note for evaluation in evaluations \
f6724c20 424 if evaluation.note is not None]
b31ce2d7 425
7d0ae1ba 426 if len(notes) > 0:
d6790a12 427 moyenne_votes = round(float(sum(notes)) / len(notes), 2)
8941aee7 428 else:
429 moyenne_votes = "Non disponible"
940c9dd8
OL
430
431 totales = len(evaluations)
432 faites = len(notes)
433
b31ce2d7 434 if obj.statut == 'REC':
940c9dd8
OL
435 if totales == faites:
436 color = "green"
437 elif faites > 0 and float(totales) / float(faites) >= 2:
438 color = "orange"
439 else:
440 color = "red"
441 else:
442 color = "black"
443
b31ce2d7
EMS
444 return """<span style="color: %s;">%s (%s/%s)</span>""" % (
445 color, moyenne_votes, faites, totales
446 )
8941aee7 447 calculer_moyenne.allow_tags = True
3eb0be91 448 calculer_moyenne.short_description = "Moyenne"
d6790a12 449 calculer_moyenne.admin_order_field = ""
8941aee7 450
7d9736ba 451 ### Permissions add, delete, change
3a542b85 452 def has_add_permission(self, request):
3383b2d1 453 user_groupes = [g.name for g in request.user.groups.all()]
26add0fd 454 if request.user.is_superuser is True or \
3383b2d1
OL
455 groups.CORRESPONDANT_RH in user_groupes or \
456 groups.DRH_NIVEAU_1 in user_groupes or \
457 groups.DRH_NIVEAU_2 in user_groupes or \
458 groups.DIRECTEUR_DE_BUREAU in user_groupes or \
459 groups.ADMINISTRATEURS in user_groupes or \
460 groups.HAUTE_DIRECTION in user_groupes:
f6724c20 461 return True
b31ce2d7 462 return False
4896b661 463
f6724c20 464 def has_delete_permission(self, request, obj=None):
3383b2d1 465 user_groupes = [g.name for g in request.user.groups.all()]
26add0fd 466 if request.user.is_superuser is True or \
3383b2d1
OL
467 groups.CORRESPONDANT_RH in user_groupes or \
468 groups.DRH_NIVEAU_1 in user_groupes or \
469 groups.DRH_NIVEAU_2 in user_groupes or \
470 groups.DIRECTEUR_DE_BUREAU in user_groupes or \
471 groups.ADMINISTRATEURS in user_groupes or \
472 groups.HAUTE_DIRECTION in user_groupes:
3a542b85 473 return True
b31ce2d7 474 return False
f6724c20
NBV
475
476 def has_change_permission(self, request, obj=None):
3383b2d1 477 user_groupes = [g.name for g in request.user.groups.all()]
26add0fd 478 if request.user.is_superuser is True or \
3383b2d1
OL
479 groups.CORRESPONDANT_RH in user_groupes or \
480 groups.DRH_NIVEAU_1 in user_groupes or \
481 groups.DRH_NIVEAU_2 in user_groupes or \
482 groups.DIRECTEUR_DE_BUREAU in user_groupes or \
483 groups.ADMINISTRATEURS in user_groupes or \
484 groups.HAUTE_DIRECTION in user_groupes:
f6724c20 485 return True
97b168d6
OL
486 return False
487
488 def get_changelist(self, request, **kwargs):
489 return OrderedChangeList
4896b661 490
d46075cb 491 def queryset(self, request):
f9983b5a 492 """
b31ce2d7
EMS
493 Spécifie un queryset limité, autrement Django exécute un
494 select_related() sans paramètre, ce qui a pour effet de charger tous
495 les objets FK, sans limite de profondeur. Dès qu'on arrive, dans les
496 modèles de Region, il existe plusieurs boucles, ce qui conduit à la
8ea41642 497 génération d'une requête infinie.
f9983b5a 498 """
b31ce2d7
EMS
499 qs = self.model._default_manager.get_query_set() \
500 .select_related('offre_emploi') \
501 .annotate(moyenne=Avg('evaluations__note'))
97b168d6 502
3383b2d1
OL
503 user_groupes = [g.name for g in request.user.groups.all()]
504 if groups.DRH_NIVEAU_1 in user_groupes or \
505 groups.DRH_NIVEAU_2 in user_groupes or \
506 groups.HAUTE_DIRECTION in user_groupes:
d6790a12 507 return qs
7d9736ba 508
3383b2d1
OL
509 if groups.DIRECTEUR_DE_BUREAU in user_groupes or \
510 groups.CORRESPONDANT_RH in user_groupes or \
511 groups.ADMINISTRATEURS in user_groupes:
512 employe = groups.get_employe_from_user(request.user)
d6790a12 513 return qs.filter(offre_emploi__region=employe.implantation.region)
7d9736ba 514
c69ecb4f 515 if Evaluateur.objects.filter(user=request.user).exists():
940c9dd8
OL
516 evaluateur = Evaluateur.objects.get(user=request.user)
517 candidat_ids = [e.candidat.id for e in
518 CandidatEvaluation.objects.filter(evaluateur=evaluateur)]
d6790a12 519 return qs.filter(id__in=candidat_ids)
b31ce2d7 520 return qs.none()
f6724c20 521
613e8dfe 522
d104b0ae
EMS
523class CandidatAdmin(VersionAdmin, CandidatAdminMixin):
524 pass
525
526
527class ProxyCandidatAdmin(CandidatAdminMixin):
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):
3383b2d1 568 user_groupes = [g.name for g in request.user.groups.all()]
71aec2c3 569 if request.user.is_superuser is True or \
3383b2d1
OL
570 groups.CORRESPONDANT_RH in user_groupes or \
571 groups.DRH_NIVEAU_1 in user_groupes or \
572 groups.DRH_NIVEAU_2 in user_groupes or \
573 groups.DIRECTEUR_DE_BUREAU in user_groupes or \
574 groups.ADMINISTRATEURS in user_groupes or \
575 groups.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):
3383b2d1 622 user_groupes = [g.name for g in request.user.groups.all()]
26add0fd 623 if request.user.is_superuser is True or \
3383b2d1
OL
624 groups.DRH_NIVEAU_1 in user_groupes or \
625 groups.DRH_NIVEAU_2 in user_groupes or \
626 groups.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):
3383b2d1 631 user_groupes = [g.name for g in request.user.groups.all()]
26add0fd 632 if request.user.is_superuser is True or \
3383b2d1
OL
633 groups.DRH_NIVEAU_1 in user_groupes or \
634 groups.DRH_NIVEAU_2 in user_groupes or \
635 groups.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):
3383b2d1 640 user_groupes = [g.name for g in request.user.groups.all()]
26add0fd 641 if request.user.is_superuser is True or \
3383b2d1
OL
642 groups.DRH_NIVEAU_1 in user_groupes or \
643 groups.DRH_NIVEAU_2 in user_groupes or \
644 groups.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 """
3383b2d1 745 user_groupes = [g.name for g in request.user.groups.all()]
f133459f 746
3bc5ea42 747 if request.user.is_superuser or \
3383b2d1
OL
748 groups.CORRESPONDANT_RH in user_groupes or \
749 groups.DRH_NIVEAU_1 in user_groupes or \
750 groups.DRH_NIVEAU_2 in user_groupes or \
751 groups.DIRECTEUR_DE_BUREAU in user_groupes or \
752 groups.ADMINISTRATEURS in user_groupes or \
753 groups.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()
3383b2d1 768 user_groupes = [g.name for g in request.user.groups.all()]
940c9dd8 769
3383b2d1
OL
770 if request.user.is_superuser or \
771 groups.CORRESPONDANT_RH in user_groupes or \
772 groups.DRH_NIVEAU_1 in user_groupes or \
773 groups.DRH_NIVEAU_2 in user_groupes or \
774 groups.DIRECTEUR_DE_BUREAU in user_groupes or \
775 groups.ADMINISTRATEURS in user_groupes or \
776 groups.HAUTE_DIRECTION in user_groupes:
940c9dd8
OL
777 return qs
778
b31ce2d7 779 evaluateur = Evaluateur.objects.get(user=request.user)
940c9dd8
OL
780 candidats_evaluations = \
781 CandidatEvaluation.objects.filter(evaluateur=evaluateur,
782 candidat__statut__in=('REC', ))
783 candidats_evaluations_ids = [ce.id for ce in candidats_evaluations]
784 return qs.filter(id__in=candidats_evaluations_ids)
596fe324 785
3bc5ea42
OL
786
787class MesCandidatEvaluationAdmin(CandidatEvaluationAdmin):
788
789 def has_change_permission(self, request, obj=None):
790 try:
791 Evaluateur.objects.get(user=request.user)
792 is_evaluateur = True
793 except:
794 is_evaluateur = False
795
5fe6986f 796 if obj is None and is_evaluateur:
3bc5ea42
OL
797 return True
798
799 try:
800 return request.user == obj.evaluateur.user
801 except:
802 return False
803
804 def queryset(self, request):
b31ce2d7
EMS
805 qs = self.model._default_manager.get_query_set() \
806 .select_related('offre_emploi')
807 evaluateur = Evaluateur.objects.get(user=request.user)
3bc5ea42
OL
808 candidats_evaluations = \
809 CandidatEvaluation.objects.filter(evaluateur=evaluateur,
810 candidat__statut__in=('REC', ))
811 candidats_evaluations_ids = [ce.id for ce in candidats_evaluations]
812 return qs.filter(id__in=candidats_evaluations_ids)
813
814
08a9b6fc 815class CourrielTemplateAdmin(BaseAdmin, VersionAdmin):
7d9736ba 816 ### Actions à afficher
a029f641
NBV
817 def get_actions(self, request):
818 actions = super(CourrielTemplateAdmin, self).get_actions(request)
819 del actions['delete_selected']
540dfae4 820 return actions
4e8340cf 821
df59fcab 822admin.site.register(OffreEmploi, OffreEmploiAdmin)
382501c1 823admin.site.register(ProxyOffreEmploi, ProxyOffreEmploiAdmin)
df59fcab 824admin.site.register(Candidat, CandidatAdmin)
382501c1 825admin.site.register(ProxyCandidat, ProxyCandidatAdmin)
720c3ad5 826admin.site.register(CandidatEvaluation, CandidatEvaluationAdmin)
3bc5ea42 827admin.site.register(MesCandidatEvaluation, MesCandidatEvaluationAdmin)
27c81d11 828admin.site.register(Evaluateur, EvaluateurAdmin)
32834000 829admin.site.register(CourrielTemplate, CourrielTemplateAdmin)