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