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