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