1829: Réorganiser les urls pour recrutement
[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.shortcuts import get_object_or_404
7 from django.core.files.storage import default_storage
8
9 from reversion.admin import VersionAdmin
10 from datamaster_modeles.models import Employe, Implantation, Region
11 from django.forms.models import BaseInlineFormSet
12
13 from recrutement.models import *
14 from recrutement.workflow import grp_administrateurs_recrutement,\
15 grp_evaluateurs_recrutement, grp_drh_recrutement
16 from recrutement.forms import *
17
18 """
19 class MetaAdmin(VersionAdmin):
20 def get_actions(self, request):
21
22 Pour refactoring
23 """
24 class OffreEmploiAdmin(VersionAdmin):
25 date_hierarchy = 'date_creation'
26 list_display = ('nom', 'resume', 'date_limite', 'region', 'statut',
27 'est_affiche', '_candidatsList')
28 list_filter = ('statut', 'est_affiche', )
29 actions = ['affecter_evaluateurs_offre_emploi', ]
30 form = OffreEmploiForm
31
32 def get_actions(self, request):
33 actions = super(OffreEmploiAdmin, self).get_actions(request)
34 del actions['delete_selected']
35 return actions
36
37 # Affecter un évaluateurs à des offres d'emploi
38 def affecter_evaluateurs_offre_emploi(modeladmin, obj, candidats):
39 selected = obj.POST.getlist(admin.ACTION_CHECKBOX_NAME)
40
41 return HttpResponseRedirect(reverse('affecter_evaluateurs_offre_emploi')+
42 "?ids=%s" % (",".join(selected)))
43 affecter_evaluateurs_offre_emploi.short_description = u'Affecter évaluateur(s)'
44
45 # Afficher la liste des candidats pour l'offre d'emploi
46 def _candidatsList(self, obj):
47 return "<a href='%s?offre_emploi__id__exact=%s'>Voir les candidats \
48 </a>" % (reverse('admin:recrutement_candidat_changelist'), obj.id)
49 _candidatsList.allow_tags = True
50 _candidatsList.short_description = "Afficher la liste des candidats"
51
52
53 def queryset(self, request):
54 qs = self.model._default_manager.get_query_set()
55 # Si user est superuser afficher toutes les offres d'emploi
56 user_groupes = request.user.groups.all()
57 if not grp_drh_recrutement in user_groupes and \
58 not request.user.is_superuser:
59 """
60 Si le user n'est ni un évaluateur ni un administrateur régional,
61 retourner none
62 Vérifier groupes
63 """
64 if grp_evaluateurs_recrutement in user_groupes:
65 try:
66 user = Evaluateur.objects.get(user=request.user)
67 except Evaluateur.DoesNotExist:
68 return qs.none()
69 elif grp_administrateurs_recrutement in user_groupes:
70 try:
71 user = AdministrateurRegional.objects.get(user=request.user)
72 except AdministrateurRegional.DoesNotExist:
73 return qs.none()
74 else:
75 return qs.none()
76
77 if type(user) is AdministrateurRegional:
78 region_ids = [g.id for g in user.regions.all()]
79 return qs.select_related('offre_emploi').\
80 filter(region__in=region_ids)
81 if type(user) is Evaluateur:
82 candidats = [g for g in user.candidats.all()]
83 offre_emploi_ids = [c.offre_emploi.id for c in candidats]
84 return qs.select_related('offre_emploi').\
85 filter(id__in=offre_emploi_ids)
86 return qs.none()
87 return qs.select_related('offre_emploi')
88
89 def has_change_permission(self, request, obj=None):
90 user_groupes = request.user.groups.all()
91 if grp_drh_recrutement in user_groupes or \
92 grp_administrateurs_recrutement in user_groupes or \
93 request.user.is_superuser:
94 return True
95 return False
96
97 class ProxyOffreEmploiAdmin(OffreEmploiAdmin):
98 list_display = ('nom', 'resume', 'date_limite', 'region', 'statut',
99 'est_affiche')
100 readonly_fields = ('description', 'bureau',
101 'duree_affectation', 'renumeration',
102 'debut_affectation', 'lieu_affectation', 'nom',
103 'resume', 'date_limite', 'region')
104 fieldsets = (
105 ('Nom', {
106 'fields': ('nom', )
107 }),
108 ('Description générale', {
109 'fields': ('resume','description', 'date_limite', )
110 }),
111 ('Coordonnées', {
112 'fields': ('lieu_affectation', 'bureau', 'region', )
113 }),
114 ('Autre', {
115 'fields': ('debut_affectation', 'duree_affectation',
116 'renumeration', )
117 }),
118 )
119
120 def get_actions(self, request):
121 actions = super(ProxyOffreEmploiAdmin, self).get_actions(request)
122 del actions['affecter_evaluateurs_offre_emploi']
123 return actions
124
125 def response_change(self, request, obj):
126 response = super(ProxyOffreEmploiAdmin, self).response_change(request, obj)
127 user_groupes = request.user.groups.all()
128 if grp_drh_recrutement in user_groupes or \
129 request.user.is_superuser:
130 return HttpResponseRedirect(reverse('admin:recrutement_offreemploi_changelist'))
131 return HttpResponseRedirect(reverse('admin:recrutement_proxyoffreemploi_changelist'))
132
133 def has_add_permission(self, request):
134 return False
135
136 def has_delete_permission(self, request, obj=None):
137 return False
138
139 def has_change_permission(self, request, obj=None):
140 user_groupes = request.user.groups.all()
141 if grp_evaluateurs_recrutement in user_groupes or \
142 grp_drh_recrutement in user_groupes or \
143 request.user.is_superuser:
144 return True
145 return False
146
147 """class ProxyEvaluateur(Evaluateur.candidats.through):
148
149 Ce proxy sert uniquement dans l'admin à disposer d'un libellé
150 plus ergonomique.
151
152 class Meta:
153 proxy = True
154 verbose_name = "évaluateur"
155 """
156 class CandidatPieceInline(admin.TabularInline):
157 model = CandidatPiece
158 fields = ('candidat', 'nom', 'path',)
159 extra = 1
160 max_num = 3
161
162 class CandidatEvaluationInlineFormSet(BaseInlineFormSet):
163 """
164 Empêche la suppression d'une évaluation pour le CandidatEvaluationInline
165 """
166 def __init__(self, *args, **kwargs):
167 super(CandidatEvaluationInlineFormSet, self).__init__(*args, **kwargs)
168 self.can_delete = False
169
170 class CandidatEvaluationInline(admin.TabularInline):
171 model = CandidatEvaluation
172 fields = ('evaluateur', 'note', 'commentaire')
173 max_num = 0
174 extra = 0
175 formset = CandidatEvaluationInlineFormSet
176
177 def get_readonly_fields(self, request, obj=None):
178 """
179 Empêche la modification des évaluations
180 """
181 if obj:
182 return self.readonly_fields+('evaluateur', 'note', 'commentaire')
183 return self.readonly_fields
184
185
186 class CandidatAdmin(VersionAdmin):
187 date_hierarchy = 'date_creation'
188 list_display = ('nom', 'prenom', 'offre_emploi','statut',
189 'voir_offre_emploi', 'calculer_moyenne',
190 'afficher_candidat',)
191 list_filter = ('offre_emploi', )
192 fieldsets = (
193 ("Offre d'emploi", {
194 'fields': ('offre_emploi', )
195 }),
196 ('Informations personnelles', {
197 'fields': ('prenom','nom','genre', 'nationalite',
198 'situation_famille', 'nombre_dependant',)
199 }),
200 ('Coordonnées', {
201 'fields': ('telephone', 'email', 'adresse', 'ville',
202 'etat_province', 'code_postal', 'pays', )
203 }),
204 ('Informations professionnelles', {
205 'fields': ('niveau_diplome','employeur_actuel',
206 'poste_actuel', 'domaine_professionnel',)
207 }),
208 ('Traitement', {
209 'fields': ('statut', )
210 }),
211 )
212 inlines = [
213 CandidatPieceInline,
214 CandidatEvaluationInline,
215 ]
216
217 actions = ['envoyer_courriel_candidats']
218
219 def get_actions(self, request):
220 actions = super(CandidatAdmin, self).get_actions(request)
221 del actions['delete_selected']
222 return actions
223
224 # Envoyer un courriel à des candidats
225 def envoyer_courriel_candidats(modeladmin, obj, candidats):
226 selected = obj.POST.getlist(admin.ACTION_CHECKBOX_NAME)
227
228 return HttpResponseRedirect(reverse('selectionner_template')+
229 "?ids=%s" % (",".join(selected)))
230 envoyer_courriel_candidats.short_description = u'Envoyer courriel'
231
232 # Évaluer un candidat
233 def evaluer_candidat(self, obj):
234 return "<a href='%s?candidat__id__exact=%s'>Évaluer le candidat</a>" % \
235 (reverse('admin:recrutement_candidatevaluation_changelist'),
236 obj.id)
237 evaluer_candidat.allow_tags = True
238 evaluer_candidat.short_description = 'Évaluation'
239
240 # Afficher un candidat
241 def afficher_candidat(self, obj):
242 return "<a href='%s'>Voir le candidat</a>" % \
243 (reverse('admin:recrutement_proxycandidat_change', args=(obj.id,)))
244 afficher_candidat.allow_tags = True
245 afficher_candidat.short_description = u'Détails du candidat'
246
247 # Voir l'offre d'emploi
248 def voir_offre_emploi(self, obj):
249 return "<a href='%s'>Voir l'offre d'emploi</a>" % \
250 (reverse('admin:recrutement_proxyoffreemploi_change',
251 args=(obj.offre_emploi.id,)))
252 voir_offre_emploi.allow_tags = True
253 voir_offre_emploi.short_description = "Afficher l'offre d'emploi"
254
255 # Calculer la moyenne des notes
256 def calculer_moyenne(self, obj):
257 evaluations = CandidatEvaluation.objects.filter(candidat=obj)
258 offre_emploi = obj.offre_emploi
259
260 notes = [evaluation.note for evaluation in evaluations.all() \
261 if evaluation.note is not None]
262
263 if len(notes) > 0 and offre_emploi.date_limite <= datetime.date.today():
264 moyenne_votes = float(sum(notes)) / len(notes)
265 else:
266 moyenne_votes = "Non disponible"
267 return moyenne_votes
268 calculer_moyenne.allow_tags = True
269 calculer_moyenne.short_description = "Moyenne des notes"
270
271 def add_delete_permission(self, request, obj=None) :
272 user_groupes = request.user.groups.all()
273 if grp_drh_recrutement in user_groupes or \
274 grp_administrateurs_recrutement in user_groupes or \
275 request.user.is_superuser:
276 return True
277 return False
278
279 def has_add_permission(self, request):
280 return self.add_delete_permission(request, request)
281
282 def has_delete_permission(self, request, obj=None):
283 return self.add_delete_permission(request, request)
284
285 def has_change_permission(self, request, obj=None):
286 user_groupes = request.user.groups.all()
287 if grp_drh_recrutement in user_groupes or \
288 grp_administrateurs_recrutement in user_groupes or \
289 request.user.is_superuser:
290 return True
291 return False
292
293 def queryset(self, request):
294 """
295 Spécifie un queryset limité, autrement Django exécute un
296 select_related() sans paramètre, ce qui a pour effet de charger tous
297 les objets FK, sans limite de profondeur. Dès qu'on arrive, dans les
298 modèles de Region, il existe plusieurs boucles, ce qui conduit à la
299 génération d'une requête infinie.
300
301 """
302 qs = self.model._default_manager.get_query_set()
303 # Si user est superuser afficher tous les candidats
304 user_groupes = request.user.groups.all()
305 if not grp_drh_recrutement in user_groupes and \
306 not request.user.is_superuser:
307 # Si le user n'est ni un évaluateur ni un administrateur régional,
308 # retourner none
309
310 # Vérifier groupes
311 if grp_evaluateurs_recrutement in user_groupes:
312 try:
313 user = Evaluateur.objects.get(user=request.user)
314 except Evaluateur.DoesNotExist:
315 return qs.none()
316 """
317 elif grp_administrateurs_recrutement in user_groupes:
318 try:
319 user = AdministrateurRegional.objects.get(user=obj.user)
320 except AdministrateurRegional.DoesNotExist:
321 return qs.none()
322 """
323 else:
324 return qs.none()
325 ids = [c.id for c in user.candidats.all()]
326 return qs.select_related('candidats').filter(id__in=ids)
327 return qs.select_related('candidats')
328
329 class ProxyCandidatAdmin(CandidatAdmin):
330 readonly_fields = ('statut', 'offre_emploi', 'prenom', 'nom',
331 'genre', 'nationalite', 'situation_famille',
332 'nombre_dependant', 'telephone', 'email', 'adresse',
333 'ville', 'etat_province', 'code_postal', 'pays',
334 'niveau_diplome', 'employeur_actuel', 'poste_actuel',
335 'domaine_professionnel', 'pieces_jointes',)
336 fieldsets = (
337 ("Offre d'emploi", {
338 'fields': ('offre_emploi', )
339 }),
340 ('Informations personnelles', {
341 'fields': ('prenom','nom','genre', 'nationalite',
342 'situation_famille', 'nombre_dependant',)
343 }),
344 ('Coordonnées', {
345 'fields': ('telephone', 'email', 'adresse', 'ville',
346 'etat_province', 'code_postal', 'pays', )
347 }),
348 ('Informations professionnelles', {
349 'fields': ('niveau_diplome','employeur_actuel',
350 'poste_actuel', 'domaine_professionnel',)
351 }),
352 )
353 inlines = []
354
355 def response_change(self, request, obj):
356 response = super(ProxyCandidatAdmin, self).response_change(request, obj)
357 user_groupes = request.user.groups.all()
358 if grp_drh_recrutement in user_groupes or \
359 request.user.is_superuser:
360 return HttpResponseRedirect(reverse('admin:recrutement_candidat_changelist'))
361 return HttpResponseRedirect(reverse('admin:recrutement_proxycandidat_changelist'))
362
363 def has_add_permission(self, request):
364 return False
365
366 def has_delete_permission(self, request, obj=None):
367 return False
368
369 def has_change_permission(self, request, obj=None):
370 user_groupes = request.user.groups.all()
371 if grp_drh_recrutement in user_groupes or \
372 grp_administrateurs_recrutement in user_groupes or \
373 grp_evaluateurs_recrutement in user_groupes or \
374 request.user.is_superuser:
375 return True
376 return False
377
378 class CandidatPieceAdmin(admin.ModelAdmin):
379 list_display = ('nom', 'candidat', )
380
381 def queryset(self, request):
382 """
383 Spécifie un queryset limité, autrement Django exécute un
384 select_related() sans paramètre, ce qui a pour effet de charger tous
385 les objets FK, sans limite de profondeur. Dès qu'on arrive, dans les
386 modèles de Region, il existe plusieurs boucles, ce qui conduit à la
387 génération d'une requête infinie.
388 Affiche la liste de candidats que si le user connecté
389 possède un Evaluateur
390 """
391 qs = self.model._default_manager.get_query_set()
392 return qs.select_related('candidat')
393
394 class EvaluateurAdmin(VersionAdmin):
395 fieldsets = (
396 ("Utilisateur", {
397 'fields': ('user',)
398 }),
399 ("Offres d'emploi à évaluer", {
400 'fields': ('offres_emploi',)
401 }),
402 )
403
404 def get_actions(self, request):
405 actions = super(EvaluateurAdmin, self).get_actions(request)
406 del actions['delete_selected']
407 return actions
408
409 class AdministrateurRegionalAdmin(VersionAdmin):
410 pass
411
412 class CandidatEvaluationAdmin(VersionAdmin):
413 list_display = ('_candidat', '_offre_emploi', 'evaluateur', '_note',
414 '_commentaire', )
415 readonly_fields = ('candidat', 'evaluateur')
416 fieldsets = (
417 ('Évaluation du candidat', {
418 'fields': ('candidat', 'evaluateur', 'note', 'commentaire', )
419 }),
420 )
421
422 def get_actions(self, request):
423 actions = super(CandidatEvaluationAdmin, self).get_actions(request)
424 del actions['delete_selected']
425 return actions
426
427 def _note(self, obj):
428 """
429 Si l'évaluateur n'a pas encore donné de note au candidat, indiquer
430 un lien pour Évaluer le candidat.
431 Sinon afficher la note.
432 """
433 evaluateur = obj.evaluateur
434 candidat = obj.candidat
435 candidat_evaluation = CandidatEvaluation.objects.\
436 get(candidat=candidat, evaluateur=evaluateur)
437 if obj.note is None:
438 return "<a href='%s'>Candidat non évalué</a>" % \
439 (reverse('admin:recrutement_candidatevaluation_change',
440 args=(candidat_evaluation.id,)))
441 return "<a href='%s'>%s</a>" % \
442 (reverse('admin:recrutement_candidatevaluation_change',
443 args=(candidat_evaluation.id,)), obj.note)
444 return
445 _note.allow_tags = True
446 _note.short_description = "Votre note"
447 _note.admin_order_field = 'note'
448
449 def _candidat(self, obj):
450 return "<a href='%s'>%s</a>" \
451 % (reverse('admin:recrutement_proxycandidat_change',
452 args=(obj.candidat.id,)), obj.candidat)
453 _candidat.allow_tags = True
454 _candidat.short_description = 'Candidat'
455
456 def _commentaire(self, obj):
457 """
458 Si l'évaluateur n'a pas encore donné de note au candidat, indiquer
459 dans le champ commentaire, Aucun au lieu de (None)
460 Sinon afficher la note.
461 """
462 if obj.commentaire is None:
463 return "Aucun"
464 return obj.commentaire
465 _commentaire.allow_tags = True
466 _commentaire.short_description = "Commentaire"
467
468
469 def _offre_emploi(self, obj):
470 return "<a href='%s'>%s</a>" % \
471 (reverse('admin:recrutement_proxyoffreemploi_change',
472 args=(obj.candidat.offre_emploi.id,)), obj.candidat.offre_emploi)
473 _offre_emploi.allow_tags = True
474 _offre_emploi.short_description = "Voir offre d'emploi"
475
476 def has_change_permission(self, request, obj=None):
477 """
478 Permettre la visualisation dans la changelist
479 mais interdire l'accès à modifier l'objet si l'évaluateur n'est pas
480 le request.user
481 """
482 return obj is None or request.user == obj.evaluateur.user
483
484 def queryset(self, request):
485 """
486 Afficher uniquement les évaluations de l'évaluateur, sauf si
487 l'utilisateur est super admin.
488 """
489 qs = self.model._default_manager.get_query_set()
490 user_groupes = request.user.groups.all()
491 if grp_drh_recrutement in user_groupes or \
492 request.user.is_superuser:
493 return qs.select_related('offre_emploi')
494
495 try:
496 evaluateur = Evaluateur.objects.get(user=request.user)
497 except Evaluateur.DoesNotExist:
498 return qs.none()
499
500 candidats_evaluations = CandidatEvaluation.objects.\
501 filter(evaluateur=evaluateur)
502 candidats_evaluations_ids = [ce.id for ce in \
503 candidats_evaluations.all()]
504 return qs.select_related('offre_emploi').\
505 filter(id__in=candidats_evaluations_ids)
506
507 class CourrielTemplateAdmin(VersionAdmin):
508 def get_actions(self, request):
509 actions = super(CourrielTemplateAdmin, self).get_actions(request)
510 del actions['delete_selected']
511 return actions
512
513 admin.site.register(OffreEmploi, OffreEmploiAdmin)
514 admin.site.register(ProxyOffreEmploi, ProxyOffreEmploiAdmin)
515 admin.site.register(Candidat, CandidatAdmin)
516 admin.site.register(ProxyCandidat, ProxyCandidatAdmin)
517 admin.site.register(CandidatEvaluation, CandidatEvaluationAdmin)
518 admin.site.register(Evaluateur, EvaluateurAdmin)
519 #admin.site.register(AdministrateurRegional, AdministrateurRegionalAdmin)
520 admin.site.register(CourrielTemplate, CourrielTemplateAdmin)