1 # -*- encoding: utf-8 -*-
4 from django
.core
.urlresolvers
import reverse
5 from django
.http
import HttpResponseRedirect
6 from django
.contrib
import admin
7 from django
.forms
.models
import BaseInlineFormSet
8 from django
.db
.models
import Avg
9 from django
.conf
import settings
11 from reversion
.admin
import VersionAdmin
12 from datamaster_modeles
.models
import Region
, Bureau
13 from project
.rh
import models
as rh
15 from project
.dae
.utils
import get_employe_from_user
as get_emp
16 from recrutement
.models
import *
17 from recrutement
.workflow
import grp_drh_recrutement
, grp_directeurs_bureau_recrutement
, \
18 grp_administrateurs_recrutement
, \
19 grp_correspondants_rh_recrutement
21 from recrutement
.forms
import *
24 IMPLANTATIONS_CENTRALES
= [15, 19]
26 class OrderedChangeList(admin
.views
.main
.ChangeList
):
28 Surcharge pour appliquer le order_by d'un annotate
30 def get_query_set(self
):
31 qs
= super(OrderedChangeList
, self
).get_query_set()
32 qs
= qs
.order_by('-moyenne')
35 class OffreEmploiAdmin(VersionAdmin
):
36 date_hierarchy
= 'date_creation'
37 list_display
= ('nom', 'date_limite', 'region', 'statut',
38 'est_affiche', '_candidatsList', )
39 exclude
= ('actif', 'poste_nom', 'resume',)
40 list_filter
= ('statut',)
41 actions
= ['affecter_evaluateurs_offre_emploi', ]
42 form
= OffreEmploiForm
44 ### Actions à afficher
45 def get_actions(self
, request
):
46 actions
= super(OffreEmploiAdmin
, self
).get_actions(request
)
47 del actions
['delete_selected']
50 ### Affecter un évaluateurs à des offres d'emploi
51 def affecter_evaluateurs_offre_emploi(modeladmin
, obj
, candidats
):
52 selected
= obj
.POST
.getlist(admin
.ACTION_CHECKBOX_NAME
)
54 return HttpResponseRedirect(reverse('affecter_evaluateurs_offre_emploi')+
55 "?ids=%s" % (",".join(selected
)))
56 affecter_evaluateurs_offre_emploi
.short_description
= u
'Affecter évaluateur(s)'
58 ### Afficher la liste des candidats pour l'offre d'emploi
59 def _candidatsList(self
, obj
):
60 return "<a href='%s?offre_emploi__id__exact=%s'>Voir les candidats \
61 </a>" % (reverse('admin:recrutement_candidat_changelist'), obj
.id)
62 _candidatsList
.allow_tags
= True
63 _candidatsList
.short_description
= "Afficher la liste des candidats"
66 def get_form(self
, request
, obj
=None, **kwargs
):
67 form
= super(OffreEmploiAdmin
, self
).get_form(request
, obj
, **kwargs
)
68 employe
= get_emp(request
.user
)
69 user_groupes
= request
.user
.groups
.all()
73 if form
.declared_fields
.has_key('region'):
74 region_field
= form
.declared_fields
['region']
76 region_field
= form
.base_fields
['region']
78 if grp_drh_recrutement
in user_groupes
:
79 region_field
.queryset
= Region
.objects
.all()
81 region_field
.queryset
= Region
.objects
.\
82 filter(id=employe
.implantation
.region
.id)
85 if form
.declared_fields
.has_key('poste'):
86 poste_field
= form
.declared_fields
['poste']
88 poste_field
= form
.base_fields
['poste']
90 if grp_drh_recrutement
in user_groupes
:
91 poste_field
.queryset
= rh
.Poste
.objects
.all()
93 poste_field
.queryset
= rh
.Poste
.objects
.\
94 filter(implantation__region
=employe
.implantation
.region
).\
95 exclude(implantation__in
=IMPLANTATIONS_CENTRALES
)
98 if form
.declared_fields
.has_key('bureau'):
99 bureau_field
= form
.declared_fields
['bureau']
101 bureau_field
= form
.base_fields
['bureau']
103 if grp_drh_recrutement
in user_groupes
:
104 bureau_field
.queryset
= Bureau
.objects
.all()
106 bureau_field
.queryset
= Bureau
.objects
.\
107 filter(region
=employe
.implantation
.region
)
112 def queryset(self
, request
):
113 qs
= self
.model
._default_manager
.get_query_set().select_related('offre_emploi')
114 user_groupes
= request
.user
.groups
.all()
115 if grp_drh_recrutement
in user_groupes
:
118 if grp_directeurs_bureau_recrutement
in user_groupes
or \
119 grp_correspondants_rh_recrutement
in user_groupes
or \
120 grp_administrateurs_recrutement
in user_groupes
:
121 employe
= get_emp(request
.user
)
122 return qs
.filter(region
=employe
.implantation
.region
)
124 if Evaluateur
.objects
.filter(user
=request
.user
).exists():
125 evaluateur
= Evaluateur
.objects
.get(user
=request
.user
)
126 offre_ids
= [e
.candidat
.offre_emploi_id
for e
in
127 CandidatEvaluation
.objects
.select_related('candidat').filter(evaluateur
=evaluateur
)]
128 return qs
.filter(id__in
=offre_ids
)
132 ### Permission add, delete, change
133 def has_add_permission(self
, request
):
134 user_groupes
= request
.user
.groups
.all()
135 if request
.user
.is_superuser
is True or \
136 grp_drh_recrutement
in user_groupes
or \
137 grp_directeurs_bureau_recrutement
in user_groupes
or \
138 grp_administrateurs_recrutement
in user_groupes
:
142 def has_delete_permission(self
, request
, obj
=None):
143 user_groupes
= request
.user
.groups
.all()
144 if request
.user
.is_superuser
is True or \
145 grp_drh_recrutement
in user_groupes
or \
146 grp_directeurs_bureau_recrutement
in user_groupes
or \
147 grp_administrateurs_recrutement
in user_groupes
:
151 def has_change_permission(self
, request
, obj
=None):
152 user_groupes
= request
.user
.groups
.all()
153 if request
.user
.is_superuser
is True or \
154 grp_drh_recrutement
in user_groupes
or \
155 grp_directeurs_bureau_recrutement
in user_groupes
or \
156 grp_administrateurs_recrutement
in user_groupes
:
160 class ProxyOffreEmploiAdmin(OffreEmploiAdmin
):
161 list_display
= ('nom', 'date_limite', 'region', 'statut',
163 readonly_fields
= ('description', 'bureau', 'duree_affectation',
164 'renumeration', 'debut_affectation', 'lieu_affectation',
165 'nom', 'resume', 'date_limite', 'region', 'poste')
170 ('Description générale', {
171 'fields': ('description', 'date_limite', )
174 'fields': ('lieu_affectation', 'bureau', 'region', 'poste',)
177 'fields': ('debut_affectation', 'duree_affectation',
184 ### Lieu de redirection après le change
185 def response_change(self
, request
, obj
):
186 return HttpResponseRedirect(reverse\
187 ('admin:recrutement_proxyoffreemploi_changelist'))
190 def get_form(self
, request
, obj
=None, **kwargs
):
191 form
= super(OffreEmploiAdmin
, self
).get_form(request
, obj
, **kwargs
)
194 ### Permissions add, delete, change
195 def has_add_permission(self
, request
):
198 def has_delete_permission(self
, request
, obj
=None):
201 def has_change_permission(self
, request
, obj
=None):
202 user_groupes
= request
.user
.groups
.all()
203 if request
.user
.is_superuser
is True or \
204 grp_correspondants_rh_recrutement
in user_groupes
or \
205 grp_drh_recrutement
in user_groupes
or \
206 grp_directeurs_bureau_recrutement
in user_groupes
or \
207 grp_administrateurs_recrutement
in user_groupes
:
215 class CandidatPieceInline(admin
.TabularInline
):
216 model
= CandidatPiece
217 fields
= ('candidat', 'nom', 'path',)
221 class ReadOnlyCandidatPieceInline(CandidatPieceInline
):
222 readonly_fields
= ('candidat', 'nom', 'path', )
226 class CandidatEvaluationInlineFormSet(BaseInlineFormSet
):
228 Empêche la suppression d'une évaluation pour le CandidatEvaluationInline
230 def __init__(self
, *args
, **kwargs
):
231 super(CandidatEvaluationInlineFormSet
, self
).__init__(*args
, **kwargs
)
232 self
.can_delete
= False
234 class CandidatEvaluationInline(admin
.TabularInline
):
235 model
= CandidatEvaluation
236 fields
= ('evaluateur', 'note', 'commentaire')
239 formset
= CandidatEvaluationInlineFormSet
242 def get_readonly_fields(self
, request
, obj
=None):
244 Empêche la modification des évaluations
247 return self
.readonly_fields
+('evaluateur', 'note', 'commentaire')
248 return self
.readonly_fields
250 class CandidatAdmin(VersionAdmin
):
251 search_fields
= ('nom', 'prenom' )
252 exclude
= ('actif', )
253 list_editable
= ('statut', )
254 list_display
= ('_candidat', 'offre_emploi',
255 'voir_offre_emploi', 'calculer_moyenne',
256 'afficher_candidat', '_date_creation', 'statut', )
257 list_filter
= ('offre_emploi', 'offre_emploi__region', 'statut', )
261 'fields': ('offre_emploi', )
263 ('Informations personnelles', {
264 'fields': ('prenom','nom','genre', 'nationalite',
265 'situation_famille', 'nombre_dependant',)
268 'fields': ('telephone', 'email', 'adresse', 'ville',
269 'etat_province', 'code_postal', 'pays', )
271 ('Informations professionnelles', {
272 'fields': ('niveau_diplome','employeur_actuel',
273 'poste_actuel', 'domaine_professionnel',)
276 'fields': ('statut', )
281 CandidatEvaluationInline
,
283 actions
= ['envoyer_courriel_candidats']
285 def _candidat(self
, obj
):
286 txt
= u
"%s %s (%s)" % ( obj
.nom
.upper(), obj
.prenom
,
288 txt
= textwrap
.wrap(txt
, 30)
289 return "<br/>".join(txt
)
290 _candidat
.short_description
= "Candidat"
291 _candidat
.admin_order_field
= "nom"
292 _candidat
.allow_tags
= True
294 def _date_creation(self
, obj
):
295 return obj
.date_creation
296 _date_creation
.admin_order_field
= "date_creation"
297 _date_creation
.short_description
= "Date de réception"
299 ### Actions à afficher
300 def get_actions(self
, request
):
301 actions
= super(CandidatAdmin
, self
).get_actions(request
)
302 del actions
['delete_selected']
305 ### Envoyer un courriel à des candidats
306 def envoyer_courriel_candidats(modeladmin
, obj
, candidats
):
307 selected
= obj
.POST
.getlist(admin
.ACTION_CHECKBOX_NAME
)
309 return HttpResponseRedirect(reverse('selectionner_template')+
310 "?ids=%s" % (",".join(selected
)))
311 envoyer_courriel_candidats
.short_description
= u
'Envoyer courriel'
313 ### Évaluer un candidat
314 def evaluer_candidat(self
, obj
):
315 return "<a href='%s?candidat__id__exact=%s'>Évaluer le candidat</a>" % \
316 (reverse('admin:recrutement_candidatevaluation_changelist'),
318 evaluer_candidat
.allow_tags
= True
319 evaluer_candidat
.short_description
= 'Évaluation'
321 ### Afficher un candidat
322 def afficher_candidat(self
, obj
):
323 items
= [u
"<li><a href='%s%s'>%s</li>" % \
324 (settings
.OE_PRIVE_MEDIA_URL
, pj
.path
, pj
.get_nom_display()) \
325 for pj
in obj
.pieces_jointes()]
326 html
= "<a href='%s'>Voir le candidat</a>" % \
327 (reverse('admin:recrutement_proxycandidat_change', args
=(obj
.id,)))
328 return "%s<ul>%s</ul>" % (html
, "\n".join(items
))
329 afficher_candidat
.allow_tags
= True
330 afficher_candidat
.short_description
= u
'Détails du candidat'
332 ### Voir l'offre d'emploi
333 def voir_offre_emploi(self
, obj
):
334 return "<a href='%s'>Voir l'offre d'emploi</a>" % \
335 (reverse('admin:recrutement_proxyoffreemploi_change',
336 args
=(obj
.offre_emploi
.id,)))
337 voir_offre_emploi
.allow_tags
= True
338 voir_offre_emploi
.short_description
= "Afficher l'offre d'emploi"
340 ### Calculer la moyenne des notes
341 def calculer_moyenne(self
, obj
):
342 evaluations
= CandidatEvaluation
.objects
.filter(candidat
=obj
)
344 notes
= [evaluation
.note
for evaluation
in evaluations \
345 if evaluation
.note
is not None]
348 moyenne_votes
= round(float(sum(notes
)) / len(notes
), 2)
350 moyenne_votes
= "Non disponible"
352 totales
= len(evaluations
)
355 if obj
.statut
== 'REC':
356 if totales
== faites
:
358 elif faites
> 0 and float(totales
) / float(faites
) >= 2:
365 return """<span style="color: %s;">%s (%s/%s)</span>""" % (color
, moyenne_votes
, faites
, totales
)
366 calculer_moyenne
.allow_tags
= True
367 calculer_moyenne
.short_description
= "Moyenne"
368 calculer_moyenne
.admin_order_field
= ""
370 ### Permissions add, delete, change
371 def has_add_permission(self
, request
):
372 user_groupes
= request
.user
.groups
.all()
373 if request
.user
.is_superuser
is True or \
374 grp_correspondants_rh_recrutement
in user_groupes
or \
375 grp_drh_recrutement
in user_groupes
or \
376 grp_directeurs_bureau_recrutement
in user_groupes
or \
377 grp_administrateurs_recrutement
in user_groupes
:
381 def has_delete_permission(self
, request
, obj
=None):
382 user_groupes
= request
.user
.groups
.all()
383 if request
.user
.is_superuser
is True or \
384 grp_correspondants_rh_recrutement
in user_groupes
or \
385 grp_drh_recrutement
in user_groupes
or \
386 grp_directeurs_bureau_recrutement
in user_groupes
or \
387 grp_administrateurs_recrutement
in user_groupes
:
391 def has_change_permission(self
, request
, obj
=None):
392 user_groupes
= request
.user
.groups
.all()
393 if request
.user
.is_superuser
is True or \
394 grp_correspondants_rh_recrutement
in user_groupes
or \
395 grp_drh_recrutement
in user_groupes
or \
396 grp_directeurs_bureau_recrutement
in user_groupes
or \
397 grp_administrateurs_recrutement
in user_groupes
:
401 def get_changelist(self
, request
, **kwargs
):
402 return OrderedChangeList
404 def queryset(self
, request
):
406 Spécifie un queryset limité, autrement Django exécute un
407 select_related() sans paramètre, ce qui a pour effet de charger tous
408 les objets FK, sans limite de profondeur. Dès qu'on arrive, dans les
409 modèles de Region, il existe plusieurs boucles, ce qui conduit à la
410 génération d'une requête infinie.
414 qs
= self
.model
._default_manager
.get_query_set().select_related('offre_emploi').annotate(moyenne
=Avg('evaluations__note'))
416 user_groupes
= request
.user
.groups
.all()
417 if grp_drh_recrutement
in user_groupes
:
420 if grp_directeurs_bureau_recrutement
in user_groupes
or \
421 grp_correspondants_rh_recrutement
in user_groupes
or \
422 grp_administrateurs_recrutement
in user_groupes
:
423 employe
= get_emp(request
.user
)
424 return qs
.filter(offre_emploi__region
=employe
.implantation
.region
)
426 if Evaluateur
.objects
.filter(user
=request
.user
).exists():
427 evaluateur
= Evaluateur
.objects
.get(user
=request
.user
)
428 candidat_ids
= [e
.candidat
.id for e
in
429 CandidatEvaluation
.objects
.filter(evaluateur
=evaluateur
)]
430 return qs
.filter(id__in
=candidat_ids
)
434 class ProxyCandidatAdmin(CandidatAdmin
):
436 readonly_fields
= ('statut', 'offre_emploi', 'prenom', 'nom',
437 'genre', 'nationalite', 'situation_famille',
438 'nombre_dependant', 'telephone', 'email', 'adresse',
439 'ville', 'etat_province', 'code_postal', 'pays',
440 'niveau_diplome', 'employeur_actuel', 'poste_actuel',
441 'domaine_professionnel', 'pieces_jointes',)
444 'fields': ('offre_emploi', )
446 ('Informations personnelles', {
447 'fields': ('prenom','nom','genre', 'nationalite',
448 'situation_famille', 'nombre_dependant',)
451 'fields': ('telephone', 'email', 'adresse', 'ville',
452 'etat_province', 'code_postal', 'pays', )
454 ('Informations professionnelles', {
455 'fields': ('niveau_diplome','employeur_actuel',
456 'poste_actuel', 'domaine_professionnel',)
459 inlines
= (CandidatEvaluationInline
, )
461 def has_add_permission(self
, request
):
464 def has_delete_permission(self
, request
, obj
=None):
467 def has_change_permission(self
, request
, obj
=None):
468 user_groupes
= request
.user
.groups
.all()
469 if request
.user
.is_superuser
is True or \
470 grp_correspondants_rh_recrutement
in user_groupes
or \
471 grp_drh_recrutement
in user_groupes
or \
472 grp_directeurs_bureau_recrutement
in user_groupes
or \
473 grp_administrateurs_recrutement
in user_groupes
:
477 evaluateur
= Evaluateur
.objects
.get(user
=request
.user
)
478 for e
in obj
.evaluations
.all():
479 if e
.evaluateur
== evaluateur
:
484 def get_actions(self
, request
):
487 class CandidatPieceAdmin(admin
.ModelAdmin
):
488 list_display
= ('nom', 'candidat', )
491 def queryset(self
, request
):
493 Spécifie un queryset limité, autrement Django exécute un
494 select_related() sans paramètre, ce qui a pour effet de charger tous
495 les objets FK, sans limite de profondeur. Dès qu'on arrive, dans les
496 modèles de Region, il existe plusieurs boucles, ce qui conduit à la
497 génération d'une requête infinie.
498 Affiche la liste de candidats que si le user connecté
499 possède un Evaluateur
501 qs
= self
.model
._default_manager
.get_query_set()
502 return qs
.select_related('candidat')
504 class EvaluateurAdmin(VersionAdmin
):
511 ### Actions à afficher
512 def get_actions(self
, request
):
513 actions
= super(EvaluateurAdmin
, self
).get_actions(request
)
514 del actions
['delete_selected']
517 ### Permissions add, delete, change
518 def has_add_permission(self
, request
):
519 user_groupes
= request
.user
.groups
.all()
520 if request
.user
.is_superuser
is True or \
521 grp_drh_recrutement
in user_groupes
:
525 def has_delete_permission(self
, request
, obj
=None):
526 user_groupes
= request
.user
.groups
.all()
527 if request
.user
.is_superuser
is True or \
528 grp_drh_recrutement
in user_groupes
:
532 def has_change_permission(self
, request
, obj
=None):
533 user_groupes
= request
.user
.groups
.all()
534 if request
.user
.is_superuser
is True or \
535 grp_drh_recrutement
in user_groupes
:
539 class CandidatEvaluationAdmin(admin
.ModelAdmin
):
540 search_fields
= ('candidat__nom', 'candidat__prenom' )
541 list_display
= ('_candidat', '_statut', '_offre_emploi', 'evaluateur', '_note',
543 readonly_fields
= ('candidat', 'evaluateur')
544 list_filter
= ('candidat__statut', 'candidat__offre_emploi',)
546 ('Évaluation du candidat', {
547 'fields': ('candidat', 'evaluateur', 'note', 'commentaire', )
551 def get_actions(self
, request
):
552 # on stocke l'evaluateur connecté (pas forcément la meilleure place...)
554 self
.evaluateur
= Evaluateur
.objects
.get(user
=request
.user
)
556 self
.evaluateur
= None
558 actions
= super(CandidatEvaluationAdmin
, self
).get_actions(request
)
559 del actions
['delete_selected']
563 def _note(self
, obj
):
565 Si l'évaluateur n'a pas encore donné de note au candidat, indiquer
566 un lien pour Évaluer le candidat.
567 Sinon afficher la note.
569 page
= self
.model
.__name__
.lower()
570 redirect_url
= 'admin:recrutement_%s_change' % page
573 label
= "Candidat non évalué"
577 if self
.evaluateur
== obj
.evaluateur
:
578 return "<a href='%s'>%s</a>" % (reverse(redirect_url
, args
=(obj
.id,)), label
)
581 _note
.allow_tags
= True
582 _note
.short_description
= "Note"
583 _note
.admin_order_field
= 'note'
585 def _statut(self
, obj
):
586 return obj
.candidat
.get_statut_display()
587 _statut
.order_field
= 'candidat__statut'
588 _statut
.short_description
= 'Statut'
591 ### Lien en lecture seule vers le candidat
592 def _candidat(self
, obj
):
593 return "<a href='%s'>%s</a>" \
594 % (reverse('admin:recrutement_proxycandidat_change',
595 args
=(obj
.candidat
.id,)), obj
.candidat
)
596 _candidat
.allow_tags
= True
597 _candidat
.short_description
= 'Candidat'
599 ### Afficher commentaire
600 def _commentaire(self
, obj
):
602 Si l'évaluateur n'a pas encore donné de note au candidat, indiquer
603 dans le champ commentaire, Aucun au lieu de (None)
604 Sinon afficher la note.
606 if obj
.commentaire
is None:
608 return obj
.commentaire
609 _commentaire
.allow_tags
= True
610 _commentaire
.short_description
= "Commentaire"
612 ### Afficher offre d'emploi
613 def _offre_emploi(self
, obj
):
614 return "<a href='%s'>%s</a>" % \
615 (reverse('admin:recrutement_proxyoffreemploi_change',
616 args
=(obj
.candidat
.offre_emploi
.id,)), obj
.candidat
.offre_emploi
)
617 _offre_emploi
.allow_tags
= True
618 _offre_emploi
.short_description
= "Voir offre d'emploi"
620 def has_add_permission(self
, request
):
623 def has_delete_permission(self
, request
, obj
=None):
626 def has_change_permission(self
, request
, obj
=None):
628 Permettre la visualisation dans la changelist
629 mais interdire l'accès à modifier l'objet si l'évaluateur n'est pas
632 user_groupes
= request
.user
.groups
.all()
634 if request
.user
.is_superuser
or \
635 grp_drh_recrutement
in user_groupes
or \
636 grp_correspondants_rh_recrutement
in user_groupes
or \
637 grp_directeurs_bureau_recrutement
in user_groupes
or \
638 grp_administrateurs_recrutement
in user_groupes
:
639 is_recrutement
= True
641 is_recrutement
= False
643 return is_recrutement
645 def queryset(self
, request
):
647 Afficher uniquement les évaluations de l'évaluateur, sauf si
648 l'utilisateur est dans les groupes suivants.
650 qs
= self
.model
._default_manager
.get_query_set().select_related('offre_emploi')
651 user_groupes
= request
.user
.groups
.all()
653 if grp_drh_recrutement
in user_groupes
or \
654 grp_correspondants_rh_recrutement
in user_groupes
or \
655 grp_directeurs_bureau_recrutement
in user_groupes
or \
656 grp_administrateurs_recrutement
in user_groupes
:
659 evaluateur
= Evaluateur
.objects
.get(user
=request
.user
)
660 candidats_evaluations
= \
661 CandidatEvaluation
.objects
.filter(evaluateur
=evaluateur
,
662 candidat__statut__in
=('REC', ))
663 candidats_evaluations_ids
= [ce
.id for ce
in candidats_evaluations
]
664 return qs
.filter(id__in
=candidats_evaluations_ids
)
667 class MesCandidatEvaluationAdmin(CandidatEvaluationAdmin
):
669 def has_change_permission(self
, request
, obj
=None):
671 Evaluateur
.objects
.get(user
=request
.user
)
674 is_evaluateur
= False
676 if obj
is None and is_evaluateur
:
680 return request
.user
== obj
.evaluateur
.user
684 def queryset(self
, request
):
685 qs
= self
.model
._default_manager
.get_query_set().select_related('offre_emploi')
686 evaluateur
= Evaluateur
.objects
.get(user
=request
.user
)
687 candidats_evaluations
= \
688 CandidatEvaluation
.objects
.filter(evaluateur
=evaluateur
,
689 candidat__statut__in
=('REC', ))
690 candidats_evaluations_ids
= [ce
.id for ce
in candidats_evaluations
]
691 return qs
.filter(id__in
=candidats_evaluations_ids
)
694 class CourrielTemplateAdmin(VersionAdmin
):
695 ### Actions à afficher
696 def get_actions(self
, request
):
697 actions
= super(CourrielTemplateAdmin
, self
).get_actions(request
)
698 del actions
['delete_selected']
701 admin
.site
.register(OffreEmploi
, OffreEmploiAdmin
)
702 admin
.site
.register(ProxyOffreEmploi
, ProxyOffreEmploiAdmin
)
703 admin
.site
.register(Candidat
, CandidatAdmin
)
704 admin
.site
.register(ProxyCandidat
, ProxyCandidatAdmin
)
705 admin
.site
.register(CandidatEvaluation
, CandidatEvaluationAdmin
)
706 admin
.site
.register(MesCandidatEvaluation
, MesCandidatEvaluationAdmin
)
707 admin
.site
.register(Evaluateur
, EvaluateurAdmin
)
708 admin
.site
.register(CourrielTemplate
, CourrielTemplateAdmin
)