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