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