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