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