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
1 # -*- encoding: utf-8 -*-
2
3 import textwrap
4
5 from auf.django.emploi.models import CandidatPiece, Candidat, OffreEmploi
6 from auf.django.references.models import Region, Bureau, Implantation
7 from django.conf import settings
8 from django.contrib import admin
9 from django.core.urlresolvers import reverse
10 from django.db.models import Avg
11 from django.shortcuts import render_to_response
12 from django.template import RequestContext
13
14 from auf.django.export.admin import ExportAdmin
15 from auf.django.emploi.models import STATUT_CHOICES
16 from django.forms.models import BaseInlineFormSet
17 from django.http import HttpResponseRedirect
18 from django.shortcuts import redirect
19 from reversion.admin import VersionAdmin
20
21 from project import groups
22
23 from project.rh import models as rh
24 from project.recrutement.forms import OffreEmploiForm
25 from project.recrutement.models import \
26 Evaluateur, CandidatEvaluation, \
27 ProxyOffreEmploi, ProxyCandidat, MesCandidatEvaluation, \
28 CourrielTemplate, OffreEmploiEvaluateur
29
30
31 ### CONSTANTES
32 IMPLANTATIONS_CENTRALES = [15, 19]
33
34
35 class BaseAdmin(admin.ModelAdmin):
36
37 class Media:
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 )
46
47
48 class 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
57
58 class OffreEmploiAdminMixin(BaseAdmin):
59 date_hierarchy = 'date_creation'
60 list_display = (
61 'nom', 'date_limite', 'region', 'statut', 'est_affiche',
62 '_candidatsList'
63 )
64 exclude = ('actif', 'poste_nom', 'resume',)
65 list_filter = ('statut',)
66 actions = ['affecter_evaluateurs_offre_emploi', ]
67 form = OffreEmploiForm
68 fieldsets = (
69 (None, {
70 'fields': (
71 'est_affiche',
72 'statut',
73 'date_limite',
74 'nom',
75 'description',
76 'poste',
77 'region',
78 'lieu_affectation',
79 'bureau',
80 'debut_affectation',
81 'duree_affectation',
82 'renumeration',
83 )
84 }),
85 )
86
87 ### Actions à afficher
88 def get_actions(self, request):
89 actions = super(OffreEmploiAdminMixin, self).get_actions(request)
90 del actions['delete_selected']
91 return actions
92
93 ### Affecter un évaluateurs à des offres d'emploi
94 def affecter_evaluateurs_offre_emploi(modeladmin, obj, candidats):
95 selected = obj.POST.getlist(admin.ACTION_CHECKBOX_NAME)
96
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)'
104
105 ### Afficher la liste des candidats pour l'offre d'emploi
106 def _candidatsList(self, obj):
107 return "<a href='%s?offre_emploi__id__exact=%s'>Voir les candidats \
108 </a>" % (reverse('admin:recrutement_proxycandidat_changelist'), obj.id)
109 _candidatsList.allow_tags = True
110 _candidatsList.short_description = "Afficher la liste des candidats"
111
112 ### Formulaire
113 def get_form(self, request, obj=None, **kwargs):
114 form = super(OffreEmploiAdminMixin, self).get_form(request, obj, **kwargs)
115 employe = groups.get_employe_from_user(request.user)
116 user_groupes = [g.name for g in request.user.groups.all()]
117
118 # Region
119 region_field = None
120 if 'region' in form.declared_fields.keys():
121 region_field = form.declared_fields['region']
122 if 'region' in form.base_fields.keys():
123 region_field = form.base_fields['region']
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.\
131 filter(id=employe.implantation.region.id)
132
133 # Poste
134 poste_field = None
135 if 'poste' in form.declared_fields.keys():
136 poste_field = form.declared_fields['poste']
137 if 'poste' in form.base_fields.keys():
138 poste_field = form.base_fields['poste']
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)
148
149 # Bureau
150 bureau_field = None
151 if 'bureau' in form.declared_fields.keys():
152 bureau_field = form.declared_fields['bureau']
153 if 'bureau' in form.base_fields.keys():
154 bureau_field = form.base_fields['bureau']
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)
163
164 return form
165
166 ### Queryset
167
168 def queryset(self, request):
169 qs = self.model._default_manager.get_query_set() \
170 .select_related('offre_emploi')
171 user_groupes = [g.name for g in request.user.groups.all()]
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:
175 return qs
176
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)
181 return qs.filter(region=employe.implantation.region)
182
183 if Evaluateur.objects.filter(user=request.user).exists():
184 evaluateur = Evaluateur.objects.get(user=request.user)
185 offre_ids = [
186 e.candidat.offre_emploi_id
187 for e in CandidatEvaluation.objects
188 .select_related('candidat')
189 .filter(evaluateur=evaluateur)
190 ]
191 return qs.filter(id__in=offre_ids)
192
193 return qs.none()
194
195 ### Permission add, delete, change
196 def has_add_permission(self, request):
197 user_groupes = [g.name for g in request.user.groups.all()]
198 if request.user.is_superuser is True or \
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:
204 return True
205 return False
206
207 def has_delete_permission(self, request, obj=None):
208 user_groupes = [g.name for g in request.user.groups.all()]
209 if request.user.is_superuser is True or \
210 groups.DRH_NIVEAU_1 in user_groupes or \
211 groups.DRH_NIVEAU_2 in user_groupes or \
212 groups.HAUTE_DIRECTION in user_groupes:
213 return True
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
222 return False
223
224 def has_change_permission(self, request, obj=None):
225 user_groupes = [g.name for g in request.user.groups.all()]
226 if request.user.is_superuser is True or \
227 groups.DRH_NIVEAU_1 in user_groupes or \
228 groups.DRH_NIVEAU_2 in user_groupes or \
229 groups.HAUTE_DIRECTION in user_groupes:
230 return True
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
244 return False
245
246 def formfield_for_foreignkey(self, db_field, request, **kwargs):
247 if db_field.name == 'lieu_affectation':
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)
254 return db_field.formfield(**kwargs)
255 return super(OffreEmploiAdminMixin, self).formfield_for_foreignkey(db_field, request, **kwargs)
256
257
258 class OffreEmploiAdmin(VersionAdmin, OffreEmploiAdminMixin):
259 pass
260
261
262 class ProxyOffreEmploiAdmin(OffreEmploiAdminMixin):
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 )
271 fieldsets = (
272 ('Nom', {
273 'fields': ('nom',)
274 }),
275 ('Description générale', {
276 'fields': ('description', 'date_limite',)
277 }),
278 ('Coordonnées', {
279 'fields': ('lieu_affectation', 'bureau', 'region', 'poste',)
280 }),
281 ('Autre', {
282 'fields': (
283 'debut_affectation', 'duree_affectation', 'renumeration',
284 )
285 }),
286 )
287 inlines = []
288
289 ### Lieu de redirection après le change
290 def response_change(self, request, obj):
291 return redirect('admin:recrutement_proxyoffreemploi_changelist')
292
293 ### Permissions add, delete, change
294 def has_add_permission(self, request):
295 return False
296
297 def has_delete_permission(self, request, obj=None):
298 return False
299
300 def has_change_permission(self, request, obj=None):
301 if obj is not None:
302 return True
303
304 return not super(ProxyOffreEmploiAdmin, self).has_change_permission(request, obj)
305
306
307 class CandidatPieceInline(admin.TabularInline):
308 model = CandidatPiece
309 fields = ('candidat', 'nom', 'path',)
310 extra = 1
311 max_num = 3
312
313
314 class ReadOnlyCandidatPieceInline(CandidatPieceInline):
315 readonly_fields = ('candidat', 'nom', 'path', )
316 cand_delete = False
317
318
319 class 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)
325 self.can_delete = False
326
327
328 class CandidatEvaluationInline(admin.TabularInline):
329 model = CandidatEvaluation
330 fields = ('evaluateur', 'note', 'commentaire')
331 max_num = 0
332 extra = 0
333 formset = CandidatEvaluationInlineFormSet
334
335 ### Fields readonly
336 def get_readonly_fields(self, request, obj=None):
337 """
338 Empêche la modification des évaluations
339 """
340 if obj:
341 return self.readonly_fields + ('evaluateur', 'note', 'commentaire')
342 return self.readonly_fields
343
344
345 class CandidatAdminMixin(BaseAdmin, ExportAdmin):
346 search_fields = ('nom', 'prenom')
347 exclude = ('actif', )
348 list_editable = ('statut', )
349 list_display = ('_candidat', 'offre_emploi',
350 'voir_offre_emploi', 'calculer_moyenne',
351 'afficher_candidat', '_date_creation', 'statut', )
352 list_filter = ('offre_emploi__nom', 'offre_emploi__region', 'statut', )
353
354 fieldsets = (
355 ("Offre d'emploi", {
356 'fields': ('offre_emploi', )
357 }),
358 ('Informations personnelles', {
359 'fields': (
360 'nom', 'prenom', 'genre', 'nationalite',
361 'situation_famille', 'nombre_dependant'
362 )
363 }),
364 ('Coordonnées', {
365 'fields': (
366 'telephone', 'email', 'adresse', 'ville', 'etat_province',
367 'code_postal', 'pays'
368 )
369 }),
370 ('Informations professionnelles', {
371 'fields': (
372 'niveau_diplome', 'employeur_actuel', 'poste_actuel',
373 'domaine_professionnel'
374 )
375 }),
376 ('Traitement', {
377 'fields': ('statut', )
378 }),
379 )
380 inlines = [
381 CandidatPieceInline,
382 CandidatEvaluationInline,
383 ]
384 actions = ['envoyer_courriel_candidats', 'changer_statut']
385
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
392 def _candidat(self, obj):
393 txt = u"%s %s (%s)" % (obj.nom.upper(), obj.prenom, obj.genre)
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
400 def _date_creation(self, obj):
401 return obj.date_creation
402 _date_creation.admin_order_field = "date_creation"
403 _date_creation.short_description = "Date de réception"
404
405 ### Actions à afficher
406 def get_actions(self, request):
407 actions = super(CandidatAdminMixin, self).get_actions(request)
408 del actions['delete_selected']
409 return actions
410
411 ### Envoyer un courriel à des candidats
412 def envoyer_courriel_candidats(modeladmin, obj, candidats):
413 selected = obj.POST.getlist(admin.ACTION_CHECKBOX_NAME)
414
415 return HttpResponseRedirect(
416 reverse('selectionner_template') + "?ids=%s" % (",".join(selected))
417 )
418 envoyer_courriel_candidats.short_description = u'Envoyer courriel'
419
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
437 ### Évaluer un candidat
438 def evaluer_candidat(self, obj):
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
445 evaluer_candidat.short_description = 'Évaluation'
446
447 ### Afficher un candidat
448 def afficher_candidat(self, obj):
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()]
452 html = "<a href='%s'>Candidature</a>" % (
453 reverse('admin:recrutement_proxycandidat_change', args=(obj.id,))
454 )
455 return "%s<ul>%s</ul>" % (html, "\n".join(items))
456 afficher_candidat.allow_tags = True
457 afficher_candidat.short_description = u'Détails du candidat'
458
459 ### Voir l'offre d'emploi
460 def voir_offre_emploi(self, obj):
461 return "<a href='%s'>Voir l'offre d'emploi</a>" % (reverse(
462 'admin:recrutement_proxyoffreemploi_change',
463 args=(obj.offre_emploi.id,)
464 ))
465 voir_offre_emploi.allow_tags = True
466 voir_offre_emploi.short_description = "Afficher l'offre d'emploi"
467
468 ### Calculer la moyenne des notes
469 def calculer_moyenne(self, obj):
470 evaluations = CandidatEvaluation.objects.filter(candidat=obj)
471
472 notes = [evaluation.note for evaluation in evaluations \
473 if evaluation.note is not None]
474
475 if len(notes) > 0:
476 moyenne_votes = round(float(sum(notes)) / len(notes), 2)
477 else:
478 moyenne_votes = "Non disponible"
479
480 totales = len(evaluations)
481 faites = len(notes)
482
483 if obj.statut == 'REC':
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
493 return """<span style="color: %s;">%s (%s/%s)</span>""" % (
494 color, moyenne_votes, faites, totales
495 )
496 calculer_moyenne.allow_tags = True
497 calculer_moyenne.short_description = "Moyenne"
498 calculer_moyenne.admin_order_field = ""
499
500 ### Permissions add, delete, change
501 def has_add_permission(self, request):
502 user_groupes = [g.name for g in request.user.groups.all()]
503 if request.user.is_superuser is True or \
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:
510 return True
511 return False
512
513 def has_delete_permission(self, request, obj=None):
514 user_groupes = [g.name for g in request.user.groups.all()]
515 if request.user.is_superuser is True or \
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:
522 return True
523 return False
524
525 def has_change_permission(self, request, obj=None):
526 user_groupes = [g.name for g in request.user.groups.all()]
527 if request.user.is_superuser is True or \
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:
534 return True
535 return False
536
537 def formfield_for_foreignkey(self, db_field, request, **kwargs):
538 if db_field.name == 'offre_emploi':
539 employe = groups.get_employe_from_user(request.user)
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
549 return db_field.formfield(**kwargs)
550 return super(CandidatAdminMixin, self).formfield_for_foreignkey(db_field, request, **kwargs)
551
552 def get_changelist(self, request, **kwargs):
553 return OrderedChangeList
554
555 def queryset(self, request):
556 """
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
561 génération d'une requête infinie.
562 """
563 qs = self.model._default_manager.get_query_set() \
564 .select_related('offre_emploi') \
565 .annotate(moyenne=Avg('evaluations__note'))
566
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:
571 return qs
572
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)
577 return qs.filter(offre_emploi__region=employe.implantation.region)
578
579 if Evaluateur.objects.filter(user=request.user).exists():
580 evaluateur = Evaluateur.objects.get(user=request.user)
581 candidat_ids = [e.candidat.id for e in
582 CandidatEvaluation.objects.filter(evaluateur=evaluateur)]
583 return qs.filter(id__in=candidat_ids)
584 return qs.none()
585
586
587 class CandidatAdmin(VersionAdmin, CandidatAdminMixin):
588 change_list_template = 'admin/recrutement/candidat/change_list.html'
589 pass
590
591
592 class ProxyCandidatAdmin(CandidatAdminMixin):
593 change_list_template = 'admin/recrutement/candidat/change_list.html'
594 list_editable = ()
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 )
602 fieldsets = (
603 ("Offre d'emploi", {
604 'fields': ('offre_emploi', )
605 }),
606 ('Informations personnelles', {
607 'fields': (
608 'prenom', 'nom', 'genre', 'nationalite', 'situation_famille',
609 'nombre_dependant'
610 )
611 }),
612 ('Coordonnées', {
613 'fields': (
614 'telephone', 'email', 'adresse', 'ville', 'etat_province',
615 'code_postal', 'pays'
616 )
617 }),
618 ('Informations professionnelles', {
619 'fields': (
620 'niveau_diplome', 'employeur_actuel', 'poste_actuel',
621 'domaine_professionnel'
622 )
623 }),
624 )
625 inlines = (CandidatEvaluationInline, )
626
627 def has_add_permission(self, request):
628 return False
629
630 def has_delete_permission(self, request, obj=None):
631 return False
632
633 def has_change_permission(self, request, obj=None):
634 if obj is not None:
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)
645
646 def get_actions(self, request):
647 return None
648
649
650 class CandidatPieceAdmin(admin.ModelAdmin):
651 list_display = ('nom', 'candidat', )
652
653 ### Queryset
654 def queryset(self, request):
655 """
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
662 """
663 qs = self.model._default_manager.get_query_set()
664 return qs.select_related('candidat')
665
666
667 class EvaluateurAdmin(BaseAdmin, VersionAdmin):
668 fieldsets = (
669 ("Utilisateur", {
670 'fields': ('user',)
671 }),
672 )
673
674 ### Actions à afficher
675 def get_actions(self, request):
676 actions = super(EvaluateurAdmin, self).get_actions(request)
677 del actions['delete_selected']
678 return actions
679
680 ### Permissions add, delete, change
681 def has_add_permission(self, request):
682 user_groupes = [g.name for g in request.user.groups.all()]
683 if request.user.is_superuser is True or \
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:
687 return True
688 return False
689
690 def has_delete_permission(self, request, obj=None):
691 user_groupes = [g.name for g in request.user.groups.all()]
692 if request.user.is_superuser is True or \
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:
696 return True
697 return False
698
699 def has_change_permission(self, request, obj=None):
700 user_groupes = [g.name for g in request.user.groups.all()]
701 if request.user.is_superuser is True or \
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:
705 return True
706 return False
707
708
709 class CandidatEvaluationAdmin(BaseAdmin):
710 search_fields = ('candidat__nom', 'candidat__prenom')
711 list_display = (
712 '_candidat', '_statut', '_offre_emploi', 'evaluateur', '_note',
713 '_commentaire'
714 )
715 readonly_fields = ('candidat', 'evaluateur')
716 list_filter = ('candidat__statut', 'candidat__offre_emploi',)
717 fieldsets = (
718 ('Évaluation du candidat', {
719 'fields': ('candidat', 'evaluateur', 'note', 'commentaire', )
720 }),
721 )
722
723 def get_actions(self, request):
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
730 actions = super(CandidatEvaluationAdmin, self).get_actions(request)
731 del actions['delete_selected']
732 return actions
733
734 ### Afficher la note
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 """
741 page = self.model.__name__.lower()
742 redirect_url = 'admin:recrutement_%s_change' % page
743
744 if obj.note is None:
745 label = "Candidat non évalué"
746 else:
747 label = obj.note
748
749 if self.evaluateur == obj.evaluateur:
750 return "<a href='%s'>%s</a>" % (
751 reverse(redirect_url, args=(obj.id,)), label
752 )
753 else:
754 return label
755 _note.allow_tags = True
756 _note.short_description = "Note"
757 _note.admin_order_field = 'note'
758
759 def _statut(self, obj):
760 return obj.candidat.get_statut_display()
761 _statut.order_field = 'candidat__statut'
762 _statut.short_description = 'Statut'
763
764 ### Lien en lecture seule vers le candidat
765 def _candidat(self, obj):
766 return "<a href='%s'>%s</a>" \
767 % (reverse('admin:recrutement_proxycandidat_change',
768 args=(obj.candidat.id,)), obj.candidat)
769 _candidat.allow_tags = True
770 _candidat.short_description = 'Candidat'
771
772 ### Afficher commentaire
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
783 _commentaire.short_description = "Commentaire"
784
785 ### Afficher offre d'emploi
786 def _offre_emploi(self, obj):
787 return "<a href='%s'>%s</a>" % \
788 (reverse('admin:recrutement_proxyoffreemploi_change',
789 args=(obj.candidat.offre_emploi.id,)), obj.candidat.offre_emploi)
790 _offre_emploi.allow_tags = True
791 _offre_emploi.short_description = "Voir offre d'emploi"
792
793 def has_add_permission(self, request):
794 return False
795
796 def has_delete_permission(self, request, obj=None):
797 return False
798
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 """
805 user_groupes = [g.name for g in request.user.groups.all()]
806
807 if request.user.is_superuser or \
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:
814 is_recrutement = True
815 else:
816 is_recrutement = False
817
818 return is_recrutement
819
820 def queryset(self, request):
821 """
822 Afficher uniquement les évaluations de l'évaluateur, sauf si
823 l'utilisateur est dans les groupes suivants.
824 """
825 qs = self.model._default_manager.get_query_set() \
826 .select_related('offre_emploi')
827 user_groupes = request.user.groups.all()
828 user_groupes = [g.name for g in request.user.groups.all()]
829
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:
837 return qs.filter(candidat__statut__in=('REC', 'SEL'))
838
839 evaluateur = Evaluateur.objects.get(user=request.user)
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)
845
846
847 class MesCandidatEvaluationAdmin(CandidatEvaluationAdmin):
848 list_filter = []
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
857 if obj is None and is_evaluateur:
858 return True
859
860 try:
861 return request.user == obj.evaluateur.user
862 except:
863 return False
864
865 def queryset(self, request):
866 qs = self.model._default_manager.get_query_set() \
867 .select_related('offre_emploi')
868 evaluateur = Evaluateur.objects.get(user=request.user)
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
876 class OffreEmploiEvaluateurAdmin(BaseAdmin):
877 pass
878
879
880 class CourrielTemplateAdmin(BaseAdmin, VersionAdmin):
881 ### Actions à afficher
882 def get_actions(self, request):
883 actions = super(CourrielTemplateAdmin, self).get_actions(request)
884 del actions['delete_selected']
885 return actions
886
887 admin.site.register(OffreEmploi, OffreEmploiAdmin)
888 admin.site.register(ProxyOffreEmploi, ProxyOffreEmploiAdmin)
889 admin.site.register(Candidat, CandidatAdmin)
890 admin.site.register(ProxyCandidat, ProxyCandidatAdmin)
891 admin.site.register(CandidatEvaluation, CandidatEvaluationAdmin)
892 admin.site.register(MesCandidatEvaluation, MesCandidatEvaluationAdmin)
893 admin.site.register(Evaluateur, EvaluateurAdmin)
894 admin.site.register(CourrielTemplate, CourrielTemplateAdmin)
895 admin.site.register(OffreEmploiEvaluateur, OffreEmploiEvaluateurAdmin)