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