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