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