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