courriel candidat
[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 def get_actions(self, request):
444 return None
445
446 class CandidatPieceAdmin(admin.ModelAdmin):
447 list_display = ('nom', 'candidat', )
448
449 ### Queryset
450 def queryset(self, request):
451 """
452 Spécifie un queryset limité, autrement Django exécute un
453 select_related() sans paramètre, ce qui a pour effet de charger tous
454 les objets FK, sans limite de profondeur. Dès qu'on arrive, dans les
455 modèles de Region, il existe plusieurs boucles, ce qui conduit à la
456 génération d'une requête infinie.
457 Affiche la liste de candidats que si le user connecté
458 possède un Evaluateur
459 """
460 qs = self.model._default_manager.get_query_set()
461 return qs.select_related('candidat')
462
463 class EvaluateurAdmin(VersionAdmin):
464 fieldsets = (
465 ("Utilisateur", {
466 'fields': ('user',)
467 }),
468 )
469
470 ### Actions à afficher
471 def get_actions(self, request):
472 actions = super(EvaluateurAdmin, self).get_actions(request)
473 del actions['delete_selected']
474 return actions
475
476 ### Permissions add, delete, change
477 def has_add_permission(self, request):
478 user_groupes = request.user.groups.all()
479 if request.user.is_superuser is True or \
480 grp_drh_recrutement in user_groupes or \
481 grp_directeurs_bureau_recrutement in user_groupes or \
482 grp_administrateurs_recrutement in user_groupes:
483 return True
484 return False
485
486 def has_delete_permission(self, request, obj=None):
487 user_groupes = request.user.groups.all()
488 if request.user.is_superuser is True or \
489 grp_drh_recrutement in user_groupes or \
490 grp_directeurs_bureau_recrutement in user_groupes or \
491 grp_administrateurs_recrutement in user_groupes:
492 return True
493 return False
494
495 def has_change_permission(self, request, obj=None):
496 user_groupes = request.user.groups.all()
497 if request.user.is_superuser is True or \
498 grp_drh_recrutement in user_groupes or \
499 grp_directeurs_bureau_recrutement in user_groupes or \
500 grp_administrateurs_recrutement in user_groupes:
501 return True
502 return False
503
504 class CandidatEvaluationAdmin(admin.ModelAdmin):
505 search_fields = ('candidat__nom', 'candidat__prenom' )
506 list_display = ('_candidat', '_statut', '_offre_emploi', 'evaluateur', '_note',
507 '_commentaire', )
508 readonly_fields = ('candidat', 'evaluateur')
509 list_filter = ('candidat__statut', 'candidat__offre_emploi',)
510 fieldsets = (
511 ('Évaluation du candidat', {
512 'fields': ('candidat', 'evaluateur', 'note', 'commentaire', )
513 }),
514 )
515
516 def get_actions(self, request):
517 # on stocke l'evaluateur connecté (pas forcément la meilleure place...)
518 try:
519 self.evaluateur = Evaluateur.objects.get(user=request.user)
520 except:
521 self.evaluateur = None
522
523 actions = super(CandidatEvaluationAdmin, self).get_actions(request)
524 del actions['delete_selected']
525 return actions
526
527 ### Afficher la note
528 def _note(self, obj):
529 """
530 Si l'évaluateur n'a pas encore donné de note au candidat, indiquer
531 un lien pour Évaluer le candidat.
532 Sinon afficher la note.
533 """
534 page = self.model.__name__.lower()
535 redirect_url = 'admin:recrutement_%s_change' % page
536
537 if obj.note is None:
538 label = "Candidat non évalué"
539 else:
540 label = obj.note
541
542 if self.evaluateur == obj.evaluateur:
543 return "<a href='%s'>%s</a>" % (reverse(redirect_url, args=(obj.id,)), label)
544 else:
545 return label
546 _note.allow_tags = True
547 _note.short_description = "Note"
548 _note.admin_order_field = 'note'
549
550 def _statut(self, obj):
551 return obj.candidat.get_statut_display()
552 _statut.order_field = 'candidat__statut'
553 _statut.short_description = 'Statut'
554
555
556 ### Lien en lecture seule vers le candidat
557 def _candidat(self, obj):
558 return "<a href='%s'>%s</a>" \
559 % (reverse('admin:recrutement_proxycandidat_change',
560 args=(obj.candidat.id,)), obj.candidat)
561 _candidat.allow_tags = True
562 _candidat.short_description = 'Candidat'
563
564 ### Afficher commentaire
565 def _commentaire(self, obj):
566 """
567 Si l'évaluateur n'a pas encore donné de note au candidat, indiquer
568 dans le champ commentaire, Aucun au lieu de (None)
569 Sinon afficher la note.
570 """
571 if obj.commentaire is None:
572 return "Aucun"
573 return obj.commentaire
574 _commentaire.allow_tags = True
575 _commentaire.short_description = "Commentaire"
576
577 ### Afficher offre d'emploi
578 def _offre_emploi(self, obj):
579 return "<a href='%s'>%s</a>" % \
580 (reverse('admin:recrutement_proxyoffreemploi_change',
581 args=(obj.candidat.offre_emploi.id,)), obj.candidat.offre_emploi)
582 _offre_emploi.allow_tags = True
583 _offre_emploi.short_description = "Voir offre d'emploi"
584
585 def has_add_permission(self, request):
586 return False
587
588 def has_delete_permission(self, request, obj=None):
589 return False
590
591 def has_change_permission(self, request, obj=None):
592 """
593 Permettre la visualisation dans la changelist
594 mais interdire l'accès à modifier l'objet si l'évaluateur n'est pas
595 le request.user
596 """
597 user_groupes = request.user.groups.all()
598
599 if request.user.is_superuser or \
600 grp_drh_recrutement in user_groupes or \
601 grp_correspondants_rh_recrutement in user_groupes or \
602 grp_directeurs_bureau_recrutement in user_groupes or \
603 grp_administrateurs_recrutement in user_groupes:
604 is_recrutement = True
605 else:
606 is_recrutement = False
607
608 try:
609 Evaluateur.objects.get(user=request.user)
610 is_evaluateur = True
611 except:
612 is_evaluateur = False
613
614 if obj is None and (is_recrutement or is_evaluateur):
615 return True
616
617 if request.user.is_superuser is True:
618 return True
619
620 try:
621 return request.user == obj.evaluateur.user
622 except:
623 return False
624
625 ### Queryset
626 def queryset(self, request):
627 """
628 Afficher uniquement les évaluations de l'évaluateur, sauf si
629 l'utilisateur est dans les groupes suivants.
630 """
631 qs = self.model._default_manager.get_query_set().select_related('offre_emploi')
632 user_groupes = request.user.groups.all()
633
634 if grp_drh_recrutement in user_groupes or \
635 grp_correspondants_rh_recrutement in user_groupes or \
636 grp_directeurs_bureau_recrutement in user_groupes or \
637 grp_administrateurs_recrutement in user_groupes:
638 return qs
639
640 evaluateur = Evaluateur.objects.get(user=request.user)
641 candidats_evaluations = \
642 CandidatEvaluation.objects.filter(evaluateur=evaluateur,
643 candidat__statut__in=('REC', ))
644 candidats_evaluations_ids = [ce.id for ce in candidats_evaluations]
645 return qs.filter(id__in=candidats_evaluations_ids)
646
647
648 class MesCandidatEvaluationAdmin(CandidatEvaluationAdmin):
649
650 def has_change_permission(self, request, obj=None):
651 try:
652 Evaluateur.objects.get(user=request.user)
653 is_evaluateur = True
654 except:
655 is_evaluateur = False
656
657 if obj is None and is_evaluateur:
658 return True
659
660 try:
661 return request.user == obj.evaluateur.user
662 except:
663 return False
664
665 def queryset(self, request):
666 qs = self.model._default_manager.get_query_set().select_related('offre_emploi')
667 evaluateur = Evaluateur.objects.get(user=request.user)
668 candidats_evaluations = \
669 CandidatEvaluation.objects.filter(evaluateur=evaluateur,
670 candidat__statut__in=('REC', ))
671 candidats_evaluations_ids = [ce.id for ce in candidats_evaluations]
672 return qs.filter(id__in=candidats_evaluations_ids)
673
674
675 class CourrielTemplateAdmin(VersionAdmin):
676 ### Actions à afficher
677 def get_actions(self, request):
678 actions = super(CourrielTemplateAdmin, self).get_actions(request)
679 del actions['delete_selected']
680 return actions
681
682 admin.site.register(OffreEmploi, OffreEmploiAdmin)
683 admin.site.register(ProxyOffreEmploi, ProxyOffreEmploiAdmin)
684 admin.site.register(Candidat, CandidatAdmin)
685 admin.site.register(ProxyCandidat, ProxyCandidatAdmin)
686 admin.site.register(CandidatEvaluation, CandidatEvaluationAdmin)
687 admin.site.register(MesCandidatEvaluation, MesCandidatEvaluationAdmin)
688 admin.site.register(Evaluateur, EvaluateurAdmin)
689 admin.site.register(CourrielTemplate, CourrielTemplateAdmin)