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