order by moyenne
[auf_rh_dae.git] / project / recrutement / admin.py
1 # -*- encoding: utf-8 -*-
2
3 from django.core.urlresolvers import reverse
4 from django.http import HttpResponseRedirect
5 from django.contrib import admin
6 from django.forms.models import BaseInlineFormSet
7 from django.db.models import Avg
8
9 from reversion.admin import VersionAdmin
10 from datamaster_modeles.models import Region, Bureau
11 from project.rh import models as rh
12
13 from project.dae.utils import get_employe_from_user as get_emp
14 from recrutement.models import *
15 from recrutement.workflow import grp_drh_recrutement, grp_directeurs_bureau_recrutement, \
16 grp_administrateurs_recrutement, \
17 grp_correspondants_rh_recrutement
18
19 from recrutement.forms import *
20
21 ### CONSTANTES
22 IMPLANTATIONS_CENTRALES = [15, 19]
23
24 class OrderedChangeList(admin.views.main.ChangeList):
25 """
26 Surcharge pour appliquer le order_by d'un annotate
27 """
28 def get_query_set(self):
29 qs = super(OrderedChangeList, self).get_query_set()
30 qs = qs.order_by('-moyenne')
31 return qs
32
33 class OffreEmploiAdmin(VersionAdmin):
34 date_hierarchy = 'date_creation'
35 list_display = ('nom', 'date_limite', 'region', 'statut',
36 'est_affiche', '_candidatsList', )
37 exclude = ('actif', 'poste_nom', 'resume',)
38 list_filter = ('statut',)
39 actions = ['affecter_evaluateurs_offre_emploi', ]
40 form = OffreEmploiForm
41
42 ### Actions à afficher
43 def get_actions(self, request):
44 actions = super(OffreEmploiAdmin, self).get_actions(request)
45 del actions['delete_selected']
46 return actions
47
48 ### Affecter un évaluateurs à des offres d'emploi
49 def affecter_evaluateurs_offre_emploi(modeladmin, obj, candidats):
50 selected = obj.POST.getlist(admin.ACTION_CHECKBOX_NAME)
51
52 return HttpResponseRedirect(reverse('affecter_evaluateurs_offre_emploi')+
53 "?ids=%s" % (",".join(selected)))
54 affecter_evaluateurs_offre_emploi.short_description = u'Affecter évaluateur(s)'
55
56 ### Afficher la liste des candidats pour l'offre d'emploi
57 def _candidatsList(self, obj):
58 return "<a href='%s?offre_emploi__id__exact=%s'>Voir les candidats \
59 </a>" % (reverse('admin:recrutement_candidat_changelist'), obj.id)
60 _candidatsList.allow_tags = True
61 _candidatsList.short_description = "Afficher la liste des candidats"
62
63 ### Formulaire
64 def get_form(self, request, obj=None, **kwargs):
65 form = super(OffreEmploiAdmin, self).get_form(request, obj, **kwargs)
66 employe = get_emp(request.user)
67 user_groupes = request.user.groups.all()
68
69
70 # Region
71 if form.declared_fields.has_key('region'):
72 region_field = form.declared_fields['region']
73 else:
74 region_field = form.base_fields['region']
75
76 if grp_drh_recrutement in user_groupes:
77 region_field.queryset = Region.objects.all()
78 else:
79 region_field.queryset = Region.objects.\
80 filter(id=employe.implantation.region.id)
81
82 # Poste
83 if form.declared_fields.has_key('poste'):
84 poste_field = form.declared_fields['poste']
85 else:
86 poste_field = form.base_fields['poste']
87
88 if grp_drh_recrutement in user_groupes:
89 poste_field.queryset = rh.Poste.objects.all()
90 else:
91 poste_field.queryset = rh.Poste.objects.\
92 filter(implantation__region=employe.implantation.region).\
93 exclude(implantation__in=IMPLANTATIONS_CENTRALES)
94
95 # Bureau
96 if form.declared_fields.has_key('bureau'):
97 bureau_field = form.declared_fields['bureau']
98 else:
99 bureau_field = form.base_fields['bureau']
100
101 if grp_drh_recrutement in user_groupes:
102 bureau_field.queryset = Bureau.objects.all()
103 else:
104 bureau_field.queryset = Bureau.objects.\
105 filter(region=employe.implantation.region)
106
107 return form
108
109 ### Queryset
110 def queryset(self, request):
111 qs = self.model._default_manager.get_query_set().select_related('offre_emploi')
112 user_groupes = request.user.groups.all()
113 if grp_drh_recrutement in user_groupes:
114 return qs
115
116 if grp_directeurs_bureau_recrutement in user_groupes or \
117 grp_correspondants_rh_recrutement in user_groupes or \
118 grp_administrateurs_recrutement in user_groupes:
119 employe = get_emp(request.user)
120 return qs.filter(region=employe.implantation.region)
121
122 if Evaluateur.objects.filter(user=request.user).exists():
123 evaluateur = Evaluateur.objects.get(user=request.user)
124 offre_ids = [e.candidat.offre_emploi_id for e in
125 CandidatEvaluation.objects.select_related('candidat').filter(evaluateur=evaluateur)]
126 return qs.filter(id__in=offre_ids)
127
128 return qs.none()
129
130 ### Permission add, delete, change
131 def has_add_permission(self, request):
132 user_groupes = request.user.groups.all()
133 if request.user.is_superuser is True or \
134 grp_drh_recrutement in user_groupes or \
135 grp_directeurs_bureau_recrutement in user_groupes or \
136 grp_administrateurs_recrutement in user_groupes:
137 return True
138 return False
139
140 def has_delete_permission(self, request, obj=None):
141 user_groupes = request.user.groups.all()
142 if request.user.is_superuser is True or \
143 grp_drh_recrutement in user_groupes or \
144 grp_directeurs_bureau_recrutement in user_groupes or \
145 grp_administrateurs_recrutement in user_groupes:
146 return True
147 return False
148
149 def has_change_permission(self, request, obj=None):
150 user_groupes = request.user.groups.all()
151 if request.user.is_superuser is True or \
152 grp_drh_recrutement in user_groupes or \
153 grp_directeurs_bureau_recrutement in user_groupes or \
154 grp_administrateurs_recrutement in user_groupes:
155 return True
156 return False
157
158 class ProxyOffreEmploiAdmin(OffreEmploiAdmin):
159 list_display = ('nom', 'date_limite', 'region', 'statut',
160 'est_affiche')
161 readonly_fields = ('description', 'bureau', 'duree_affectation',
162 'renumeration', 'debut_affectation', 'lieu_affectation',
163 'nom', 'resume', 'date_limite', 'region', 'poste')
164 fieldsets = (
165 ('Nom', {
166 'fields': ('nom', )
167 }),
168 ('Description générale', {
169 'fields': ('description', 'date_limite', )
170 }),
171 ('Coordonnées', {
172 'fields': ('lieu_affectation', 'bureau', 'region', 'poste',)
173 }),
174 ('Autre', {
175 'fields': ('debut_affectation', 'duree_affectation',
176 'renumeration', )
177 }),
178 )
179 inlines = []
180
181
182 ### Lieu de redirection après le change
183 def response_change(self, request, obj):
184 return HttpResponseRedirect(reverse\
185 ('admin:recrutement_proxyoffreemploi_changelist'))
186
187 ### Formulaire
188 def get_form(self, request, obj=None, **kwargs):
189 form = super(OffreEmploiAdmin, self).get_form(request, obj, **kwargs)
190 return form
191
192 ### Permissions add, delete, change
193 def has_add_permission(self, request):
194 return False
195
196 def has_delete_permission(self, request, obj=None):
197 return False
198
199 def has_change_permission(self, request, obj=None):
200 return True
201
202 class CandidatPieceInline(admin.TabularInline):
203 model = CandidatPiece
204 fields = ('candidat', 'nom', 'path',)
205 extra = 1
206 max_num = 3
207
208 class ReadOnlyCandidatPieceInline(CandidatPieceInline):
209 readonly_fields = ('candidat', 'nom', 'path', )
210 cand_delete = False
211
212
213 class CandidatEvaluationInlineFormSet(BaseInlineFormSet):
214 """
215 Empêche la suppression d'une évaluation pour le CandidatEvaluationInline
216 """
217 def __init__(self, *args, **kwargs):
218 super(CandidatEvaluationInlineFormSet, self).__init__(*args, **kwargs)
219 self.can_delete = False
220
221 class CandidatEvaluationInline(admin.TabularInline):
222 model = CandidatEvaluation
223 fields = ('evaluateur', 'note', 'commentaire')
224 max_num = 0
225 extra = 0
226 formset = CandidatEvaluationInlineFormSet
227
228 ### Fields readonly
229 def get_readonly_fields(self, request, obj=None):
230 """
231 Empêche la modification des évaluations
232 """
233 if obj:
234 return self.readonly_fields+('evaluateur', 'note', 'commentaire')
235 return self.readonly_fields
236
237 class CandidatAdmin(VersionAdmin):
238 search_fields = ('nom', 'prenom' )
239 exclude = ('actif', )
240 list_editable = ('statut', )
241 list_display = ('nom', 'prenom', 'offre_emploi',
242 'voir_offre_emploi', 'calculer_moyenne',
243 'afficher_candidat', '_date_creation', 'statut', )
244 list_filter = ('offre_emploi', 'offre_emploi__region', 'statut', )
245
246 fieldsets = (
247 ("Offre d'emploi", {
248 'fields': ('offre_emploi', )
249 }),
250 ('Informations personnelles', {
251 'fields': ('prenom','nom','genre', 'nationalite',
252 'situation_famille', 'nombre_dependant',)
253 }),
254 ('Coordonnées', {
255 'fields': ('telephone', 'email', 'adresse', 'ville',
256 'etat_province', 'code_postal', 'pays', )
257 }),
258 ('Informations professionnelles', {
259 'fields': ('niveau_diplome','employeur_actuel',
260 'poste_actuel', 'domaine_professionnel',)
261 }),
262 ('Traitement', {
263 'fields': ('statut', )
264 }),
265 )
266 inlines = [
267 CandidatPieceInline,
268 CandidatEvaluationInline,
269 ]
270
271 actions = ['envoyer_courriel_candidats']
272
273 def _date_creation(self, obj):
274 return obj.date_creation
275 _date_creation.order_field = "date_creation"
276 _date_creation.short_description = "Date de création"
277
278 ### Actions à afficher
279 def get_actions(self, request):
280 actions = super(CandidatAdmin, self).get_actions(request)
281 del actions['delete_selected']
282 return actions
283
284 ### Envoyer un courriel à des candidats
285 def envoyer_courriel_candidats(modeladmin, obj, candidats):
286 selected = obj.POST.getlist(admin.ACTION_CHECKBOX_NAME)
287
288 return HttpResponseRedirect(reverse('selectionner_template')+
289 "?ids=%s" % (",".join(selected)))
290 envoyer_courriel_candidats.short_description = u'Envoyer courriel'
291
292 ### Évaluer un candidat
293 def evaluer_candidat(self, obj):
294 return "<a href='%s?candidat__id__exact=%s'>Évaluer le candidat</a>" % \
295 (reverse('admin:recrutement_candidatevaluation_changelist'),
296 obj.id)
297 evaluer_candidat.allow_tags = True
298 evaluer_candidat.short_description = 'Évaluation'
299
300 ### Afficher un candidat
301 def afficher_candidat(self, obj):
302 return "<a href='%s'>Voir le candidat</a>" % \
303 (reverse('admin:recrutement_proxycandidat_change', args=(obj.id,)))
304 afficher_candidat.allow_tags = True
305 afficher_candidat.short_description = u'Détails du candidat'
306
307 ### Voir l'offre d'emploi
308 def voir_offre_emploi(self, obj):
309 return "<a href='%s'>Voir l'offre d'emploi</a>" % \
310 (reverse('admin:recrutement_proxyoffreemploi_change',
311 args=(obj.offre_emploi.id,)))
312 voir_offre_emploi.allow_tags = True
313 voir_offre_emploi.short_description = "Afficher l'offre d'emploi"
314
315 ### Calculer la moyenne des notes
316 def calculer_moyenne(self, obj):
317 evaluations = CandidatEvaluation.objects.filter(candidat=obj)
318
319 notes = [evaluation.note for evaluation in evaluations \
320 if evaluation.note is not None]
321
322 if len(notes) > 0:
323 moyenne_votes = round(float(sum(notes)) / len(notes), 2)
324 else:
325 moyenne_votes = "Non disponible"
326
327 totales = len(evaluations)
328 faites = len(notes)
329
330 if obj.statut == 'REC':
331 if totales == faites:
332 color = "green"
333 elif faites > 0 and float(totales) / float(faites) >= 2:
334 color = "orange"
335 else:
336 color = "red"
337 else:
338 color = "black"
339
340 return """<span style="color: %s;">%s (%s/%s)</span>""" % (color, moyenne_votes, faites, totales)
341 calculer_moyenne.allow_tags = True
342 calculer_moyenne.short_description = "Notation"
343 calculer_moyenne.admin_order_field = ""
344
345 ### Permissions add, delete, change
346 def has_add_permission(self, request):
347 user_groupes = request.user.groups.all()
348 if request.user.is_superuser is True or \
349 grp_drh_recrutement in user_groupes or \
350 grp_directeurs_bureau_recrutement in user_groupes or \
351 grp_administrateurs_recrutement in user_groupes:
352 return True
353 return False
354
355 def has_delete_permission(self, request, obj=None):
356 user_groupes = request.user.groups.all()
357 if request.user.is_superuser is True or \
358 grp_drh_recrutement in user_groupes or \
359 grp_directeurs_bureau_recrutement in user_groupes or \
360 grp_administrateurs_recrutement in user_groupes:
361 return True
362 return False
363
364 def has_change_permission(self, request, obj=None):
365 user_groupes = request.user.groups.all()
366 if request.user.is_superuser is True or \
367 grp_correspondants_rh_recrutement in user_groupes or \
368 grp_drh_recrutement in user_groupes or \
369 grp_directeurs_bureau_recrutement in user_groupes or \
370 grp_administrateurs_recrutement in user_groupes:
371 return True
372 return False
373
374 def get_changelist(self, request, **kwargs):
375 return OrderedChangeList
376
377 def queryset(self, request):
378 """
379 Spécifie un queryset limité, autrement Django exécute un
380 select_related() sans paramètre, ce qui a pour effet de charger tous
381 les objets FK, sans limite de profondeur. Dès qu'on arrive, dans les
382 modèles de Region, il existe plusieurs boucles, ce qui conduit à la
383 génération d'une requête infinie.
384
385 """
386
387 qs = self.model._default_manager.get_query_set().select_related('offre_emploi').annotate(moyenne=Avg('evaluations__note'))
388
389 user_groupes = request.user.groups.all()
390 if grp_drh_recrutement in user_groupes:
391 return qs
392
393 if grp_directeurs_bureau_recrutement in user_groupes or \
394 grp_correspondants_rh_recrutement in user_groupes or \
395 grp_administrateurs_recrutement in user_groupes:
396 employe = get_emp(request.user)
397 return qs.filter(offre_emploi__region=employe.implantation.region)
398
399 if Evaluateur.objects.filter(user=request.user).exists():
400 evaluateur = Evaluateur.objects.get(user=request.user)
401 candidat_ids = [e.candidat.id for e in
402 CandidatEvaluation.objects.filter(evaluateur=evaluateur)]
403 return qs.filter(id__in=candidat_ids)
404 return qs.none()
405
406
407 class ProxyCandidatAdmin(CandidatAdmin):
408 list_editable = ()
409 readonly_fields = ('statut', 'offre_emploi', 'prenom', 'nom',
410 'genre', 'nationalite', 'situation_famille',
411 'nombre_dependant', 'telephone', 'email', 'adresse',
412 'ville', 'etat_province', 'code_postal', 'pays',
413 'niveau_diplome', 'employeur_actuel', 'poste_actuel',
414 'domaine_professionnel', 'pieces_jointes',)
415 fieldsets = (
416 ("Offre d'emploi", {
417 'fields': ('offre_emploi', )
418 }),
419 ('Informations personnelles', {
420 'fields': ('prenom','nom','genre', 'nationalite',
421 'situation_famille', 'nombre_dependant',)
422 }),
423 ('Coordonnées', {
424 'fields': ('telephone', 'email', 'adresse', 'ville',
425 'etat_province', 'code_postal', 'pays', )
426 }),
427 ('Informations professionnelles', {
428 'fields': ('niveau_diplome','employeur_actuel',
429 'poste_actuel', 'domaine_professionnel',)
430 }),
431 )
432 inlines = (CandidatEvaluationInline, )
433
434 def has_add_permission(self, request):
435 return False
436
437 def has_delete_permission(self, request, obj=None):
438 return False
439
440 def has_change_permission(self, request, obj=None):
441 return True
442
443 class CandidatPieceAdmin(admin.ModelAdmin):
444 list_display = ('nom', 'candidat', )
445
446 ### Queryset
447 def queryset(self, request):
448 """
449 Spécifie un queryset limité, autrement Django exécute un
450 select_related() sans paramètre, ce qui a pour effet de charger tous
451 les objets FK, sans limite de profondeur. Dès qu'on arrive, dans les
452 modèles de Region, il existe plusieurs boucles, ce qui conduit à la
453 génération d'une requête infinie.
454 Affiche la liste de candidats que si le user connecté
455 possède un Evaluateur
456 """
457 qs = self.model._default_manager.get_query_set()
458 return qs.select_related('candidat')
459
460 class EvaluateurAdmin(VersionAdmin):
461 fieldsets = (
462 ("Utilisateur", {
463 'fields': ('user',)
464 }),
465 )
466
467 ### Actions à afficher
468 def get_actions(self, request):
469 actions = super(EvaluateurAdmin, self).get_actions(request)
470 del actions['delete_selected']
471 return actions
472
473 ### Permissions add, delete, change
474 def has_add_permission(self, request):
475 user_groupes = request.user.groups.all()
476 if request.user.is_superuser is True or \
477 grp_drh_recrutement in user_groupes or \
478 grp_directeurs_bureau_recrutement in user_groupes or \
479 grp_administrateurs_recrutement in user_groupes:
480 return True
481 return False
482
483 def has_delete_permission(self, request, obj=None):
484 user_groupes = request.user.groups.all()
485 if request.user.is_superuser is True or \
486 grp_drh_recrutement in user_groupes or \
487 grp_directeurs_bureau_recrutement in user_groupes or \
488 grp_administrateurs_recrutement in user_groupes:
489 return True
490 return False
491
492 def has_change_permission(self, request, obj=None):
493 user_groupes = request.user.groups.all()
494 if request.user.is_superuser is True or \
495 grp_drh_recrutement in user_groupes or \
496 grp_directeurs_bureau_recrutement in user_groupes or \
497 grp_administrateurs_recrutement in user_groupes:
498 return True
499 return False
500
501 class CandidatEvaluationAdmin(admin.ModelAdmin):
502 search_fields = ('candidat__nom', 'candidat__prenom' )
503 list_display = ('_candidat', '_statut', '_offre_emploi', 'evaluateur', '_note',
504 '_commentaire', )
505 readonly_fields = ('candidat', 'evaluateur')
506 list_filter = ('candidat__statut', 'candidat__offre_emploi',)
507 fieldsets = (
508 ('Évaluation du candidat', {
509 'fields': ('candidat', 'evaluateur', 'note', 'commentaire', )
510 }),
511 )
512
513 def get_actions(self, request):
514 # on stocke l'evaluateur connecté (pas forcément la meilleure place...)
515 try:
516 self.evaluateur = Evaluateur.objects.get(user=request.user)
517 except:
518 self.evaluateur = None
519
520 actions = super(CandidatEvaluationAdmin, self).get_actions(request)
521 del actions['delete_selected']
522 return actions
523
524 ### Afficher la note
525 def _note(self, obj):
526 """
527 Si l'évaluateur n'a pas encore donné de note au candidat, indiquer
528 un lien pour Évaluer le candidat.
529 Sinon afficher la note.
530 """
531 page = self.model.__name__.lower()
532 redirect_url = 'admin:recrutement_%s_change' % page
533
534 if obj.note is None:
535 label = "Candidat non évalué"
536 else:
537 label = obj.note
538
539 if self.evaluateur == obj.evaluateur:
540 return "<a href='%s'>%s</a>" % (reverse(redirect_url, args=(obj.id,)), label)
541 else:
542 return label
543 _note.allow_tags = True
544 _note.short_description = "Note"
545 _note.admin_order_field = 'note'
546
547 def _statut(self, obj):
548 return obj.candidat.get_statut_display()
549 _statut.order_field = 'candidat__statut'
550 _statut.short_description = 'Statut'
551
552
553 ### Lien en lecture seule vers le candidat
554 def _candidat(self, obj):
555 return "<a href='%s'>%s</a>" \
556 % (reverse('admin:recrutement_proxycandidat_change',
557 args=(obj.candidat.id,)), obj.candidat)
558 _candidat.allow_tags = True
559 _candidat.short_description = 'Candidat'
560
561 ### Afficher commentaire
562 def _commentaire(self, obj):
563 """
564 Si l'évaluateur n'a pas encore donné de note au candidat, indiquer
565 dans le champ commentaire, Aucun au lieu de (None)
566 Sinon afficher la note.
567 """
568 if obj.commentaire is None:
569 return "Aucun"
570 return obj.commentaire
571 _commentaire.allow_tags = True
572 _commentaire.short_description = "Commentaire"
573
574 ### Afficher offre d'emploi
575 def _offre_emploi(self, obj):
576 return "<a href='%s'>%s</a>" % \
577 (reverse('admin:recrutement_proxyoffreemploi_change',
578 args=(obj.candidat.offre_emploi.id,)), obj.candidat.offre_emploi)
579 _offre_emploi.allow_tags = True
580 _offre_emploi.short_description = "Voir offre d'emploi"
581
582 def has_add_permission(self, request):
583 return False
584
585 def has_delete_permission(self, request, obj=None):
586 return False
587
588 def has_change_permission(self, request, obj=None):
589 """
590 Permettre la visualisation dans la changelist
591 mais interdire l'accès à modifier l'objet si l'évaluateur n'est pas
592 le request.user
593 """
594 user_groupes = request.user.groups.all()
595
596 if request.user.is_superuser or \
597 grp_drh_recrutement in user_groupes or \
598 grp_correspondants_rh_recrutement in user_groupes or \
599 grp_directeurs_bureau_recrutement in user_groupes or \
600 grp_administrateurs_recrutement in user_groupes:
601 is_recrutement = True
602 else:
603 is_recrutement = False
604
605 try:
606 Evaluateur.objects.get(user=request.user)
607 is_evaluateur = True
608 except:
609 is_evaluateur = False
610
611 if obj is None and (is_recrutement or is_evaluateur):
612 return True
613
614 if request.user.is_superuser is True:
615 return True
616
617 try:
618 return request.user == obj.evaluateur.user
619 except:
620 return False
621
622 ### Queryset
623 def queryset(self, request):
624 """
625 Afficher uniquement les évaluations de l'évaluateur, sauf si
626 l'utilisateur est dans les groupes suivants.
627 """
628 qs = self.model._default_manager.get_query_set().select_related('offre_emploi')
629 user_groupes = request.user.groups.all()
630
631 if grp_drh_recrutement in user_groupes or \
632 grp_correspondants_rh_recrutement in user_groupes or \
633 grp_directeurs_bureau_recrutement in user_groupes or \
634 grp_administrateurs_recrutement in user_groupes:
635 return qs
636
637 evaluateur = Evaluateur.objects.get(user=request.user)
638 candidats_evaluations = \
639 CandidatEvaluation.objects.filter(evaluateur=evaluateur,
640 candidat__statut__in=('REC', ))
641 candidats_evaluations_ids = [ce.id for ce in candidats_evaluations]
642 return qs.filter(id__in=candidats_evaluations_ids)
643
644
645 class MesCandidatEvaluationAdmin(CandidatEvaluationAdmin):
646
647 def has_change_permission(self, request, obj=None):
648 try:
649 Evaluateur.objects.get(user=request.user)
650 is_evaluateur = True
651 except:
652 is_evaluateur = False
653
654 if obj is None and is_evaluateur:
655 return True
656
657 try:
658 return request.user == obj.evaluateur.user
659 except:
660 return False
661
662 def queryset(self, request):
663 qs = self.model._default_manager.get_query_set().select_related('offre_emploi')
664 evaluateur = Evaluateur.objects.get(user=request.user)
665 candidats_evaluations = \
666 CandidatEvaluation.objects.filter(evaluateur=evaluateur,
667 candidat__statut__in=('REC', ))
668 candidats_evaluations_ids = [ce.id for ce in candidats_evaluations]
669 return qs.filter(id__in=candidats_evaluations_ids)
670
671
672 class CourrielTemplateAdmin(VersionAdmin):
673 ### Actions à afficher
674 def get_actions(self, request):
675 actions = super(CourrielTemplateAdmin, self).get_actions(request)
676 del actions['delete_selected']
677 return actions
678
679 admin.site.register(OffreEmploi, OffreEmploiAdmin)
680 admin.site.register(ProxyOffreEmploi, ProxyOffreEmploiAdmin)
681 admin.site.register(Candidat, CandidatAdmin)
682 admin.site.register(ProxyCandidat, ProxyCandidatAdmin)
683 admin.site.register(CandidatEvaluation, CandidatEvaluationAdmin)
684 admin.site.register(MesCandidatEvaluation, MesCandidatEvaluationAdmin)
685 admin.site.register(Evaluateur, EvaluateurAdmin)
686 admin.site.register(CourrielTemplate, CourrielTemplateAdmin)