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