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