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