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