PEP8
[auf_rh_dae.git] / project / rh / views.py
CommitLineData
e9bbd6ba 1# -*- encoding: utf-8 -*-
22343fe7 2
e90b19fd 3import urllib
c9122f1d 4from datetime import date
eabb27fd 5import StringIO
9b0a8554 6
9f55f18a 7import pygraphviz as pgv
e949324b 8
e90b19fd 9from django import forms
4ba84959 10from django.conf import settings
e90b19fd 11from django.contrib.auth.decorators import login_required
eabb27fd 12from django.core.servers.basehttp import FileWrapper
dcd1b959 13from django.core.urlresolvers import reverse
fbe60a9d 14from django.db.models import Q
4ba84959 15from django.http import HttpResponse
890b80e7 16from django.shortcuts import render, get_object_or_404
e9bbd6ba 17
4c17e518 18from auf.django.references import models as ref
e9bbd6ba 19
018c8eaf 20from project.decorators import drh_or_admin_required
ae99002a 21from project.decorators import region_protected
afd3be54 22from project.groups import get_employe_from_user
a4198848 23from project.groups import grp_drh, grp_correspondants_rh
e9bbd6ba 24
4ba84959 25from project.rh import models as rh
4ba84959 26from project.rh import graph as rh_graph
890b80e7 27from project.rh.change_list import RechercheTemporelle
10a11035 28from project.rh.lib import get_lookup_params
4ba84959 29from project.rh.masse_salariale import MasseSalariale
890b80e7 30from project.rh.templatetags.rapports import SortHeaders
9817fed5 31
22343fe7 32
c9122f1d 33@login_required
34def profil(request):
35 """Profil personnel de l'employé - éditable"""
10a11035 36 employe = get_employe_from_user(request.user)
7d0c2206 37 c = {
8b3f6ff5
DB
38 'user': request.user,
39 'employe': employe,
7d0c2206 40 }
890b80e7 41 return render(request, 'rh/profil.html', c)
22343fe7
OL
42
43
3411ac33 44@login_required
a9faef67 45def employes_liste(request):
46 """Liste des employés."""
5ea6b5bb 47 today = date.today()
48 employes = rh.Employe.objects \
5ea6b5bb 49 .exclude(dossiers__date_debut__gt=today) \
50 .exclude(dossiers__date_fin__lt=today) \
51 .order_by('nom')
a9faef67 52 c = {
22343fe7
OL
53 'user': request.user,
54 'employes': employes,
a9faef67 55 }
890b80e7 56 return render(request, 'rh/employes_liste.html', c)
22343fe7
OL
57
58
3411ac33 59@login_required
a9faef67 60def employe(request, id):
61 """Information publique sur un employé."""
62 try:
63 employe = rh.Employe.objects.get(pk=id)
64 except:
65 employe = rh.Employe.objects.none()
66 c = {
22343fe7
OL
67 'user': request.user,
68 'employe': employe,
a9faef67 69 }
890b80e7 70 return render(request, 'rh/employe.html', c)
63e17dff
PP
71
72
08faf06e
JPC
73@login_required
74@drh_or_admin_required
f2d65e83 75def rapports_contrat(request):
dd18f6e3 76 # statut default = actif?
dcd1b959 77 if 'HTTP_REFERER' in request.META.keys():
58348377
EMS
78 referer = request.META['HTTP_REFERER']
79 referer = "/".join(referer.split('/')[3:])
80 referer = "/%s" % referer.split('?')[0]
81 if referer != reverse('rhr_contrats'):
82 params = request.GET.copy()
83 params.update({'statut': 'Actif'})
84 request.GET = params
f2d65e83 85
00ca4d9f
EMS
86 contrats = rh.Contrat.objects.select_related(
87 'dossier', 'dossier__poste', 'dossier__poste__implantation',
88 'type_contrat', 'dossier__employe'
89 )
22343fe7 90
dd18f6e3 91 # filters et recherche temporelle
6bee05ff 92 cl = RechercheTemporelle(dict(request.GET.items()), rh.Contrat)
dd18f6e3 93 lookup_params = get_lookup_params(request)
f0f6b03e 94 lookup_params = cl.purge_params(lookup_params)
dcd1b959
OL
95 q_temporel = cl.get_q_temporel(contrats)
96 q = Q(**lookup_params) & q_temporel
97 contrats = contrats.filter(q).exclude(dossier__employe__supprime=1)
f0f6b03e 98
dd18f6e3 99 # tri
65e02621 100 if 'o' in request.GET:
00ca4d9f
EMS
101 contrats = contrats.order_by(
102 ('-' if request.GET.get('ot') == "desc" else '') + request.GET['o']
103 )
f2d65e83 104
dd18f6e3 105 # affichage
8ee135b7
JPC
106 employes = set([c.dossier.employe_id for c in contrats])
107
65e02621 108 headers = [
dd18f6e3 109 ("dossier__employe__id", u"#"),
d50a5e5d 110 ("dossier__employe__nom", u"Employé"),
dd18f6e3
DB
111 ("dossier", u"Dossier : Poste"),
112 ("type_contrat__nom", u"Contrat"),
113 ("date_debut", u"Début contrat"),
114 ("date_fin", u"Fin contrat"),
115 ("dossier__statut_residence", u"Statut"),
d50a5e5d
OL
116 ("dossier__poste__implantation__region", u"Région"),
117 ("dossier__poste__implantation", u"Implantation"),
65e02621 118 ]
dd18f6e3
DB
119 h = SortHeaders(
120 request, headers, order_field_type="ot", order_field="o",
121 not_sortable=('dossier',)
122 )
65e02621 123
f2d65e83 124 c = {
00ca4d9f 125 'cl': cl,
f2d65e83
PP
126 'title': 'Rapport des contrats',
127 'contrats': contrats,
00ca4d9f 128 'count_employe': len(employes),
65e02621 129 'headers': list(h.headers()),
f2d65e83 130 }
890b80e7 131 return render(request, 'rh/rapports/contrats.html', c)
e2c0b1ac
PP
132
133
02c1b3dc
JPC
134@login_required
135@drh_or_admin_required
98d6eb6c 136def rapports_masse_salariale(request):
df37184c 137
e90b19fd 138 class RechercheTemporelle(forms.Form):
aecb222b 139 CHOICE_ANNEES = range(
00ca4d9f
EMS
140 rh.Remuneration.objects.exclude(date_debut=None)
141 .order_by('date_debut')[0].date_debut.year,
142 date.today().year + 1
143 )
144
145 annee = forms.CharField(
146 initial=date.today().year,
147 widget=forms.Select(
148 choices=((a, a) for a in reversed(CHOICE_ANNEES))
149 )
444f3223 150 )
aecb222b 151
48c0abfe
JPC
152 region = forms.CharField(
153 widget=forms.Select(choices=[('', '')] +
154 [(i.id, i) for i in ref.Region.objects.all()]
155 )
156 )
157
158 implantation = forms.CharField(
159 widget=forms.Select(choices=[('', '')] +
160 [(i.id, i) for i in ref.Implantation.objects.all()]
161 )
162 )
163
aecb222b
JPC
164 #date_debut = forms.DateField(widget=adminwidgets.AdminDateWidget)
165 #date_fin = forms.DateField(widget=adminwidgets.AdminDateWidget)
e90b19fd 166
e90b19fd 167 form = RechercheTemporelle(request.GET)
00ca4d9f
EMS
168 get_filtre = [
169 (k, v) for k, v in request.GET.items()
170 if k not in ('date_debut', 'date_fin', 'implantation')
171 ]
e90b19fd
OL
172 query_string = urllib.urlencode(get_filtre)
173
aecb222b
JPC
174 date_debut = None
175 date_fin = None
444f3223
JPC
176 if request.GET.get('annee', None):
177 date_debut = "01-01-%s" % request.GET.get('annee', None)
178 date_fin = "31-12-%s" % request.GET.get('annee', None)
df37184c 179
48c0abfe
JPC
180 implantation = request.GET.get('implantation')
181 region = request.GET.get('region')
df37184c
JPC
182
183 custom_filter = {}
184 if implantation:
185 custom_filter['dossier__poste__implantation'] = implantation
186 if region:
187 custom_filter['dossier__poste__implantation__region'] = region
188
df37184c 189 c = {
e90b19fd
OL
190 'title': 'Rapport de masse salariale',
191 'form': form,
df37184c 192 'headers': [],
e90b19fd 193 'query_string': query_string,
df37184c 194 }
22b53270 195 if date_debut or date_fin:
c99116c3
JPC
196 masse = MasseSalariale(date_debut, date_fin, custom_filter,
197 request.GET.get('ne_pas_grouper', False))
515b83c3 198 if masse.rapport:
eabb27fd 199 if request.GET.get('ods'):
00ca4d9f
EMS
200 for h in (
201 h for h in masse.headers if 'background-color' in h[2]
202 ):
eabb27fd
JPC
203 del h[2]['background-color']
204 masse.ods()
205 output = StringIO.StringIO()
206 masse.doc.save(output)
207 output.seek(0)
208
00ca4d9f
EMS
209 response = HttpResponse(
210 FileWrapper(output),
211 content_type=(
212 'application/vnd.oasis.opendocument.spreadsheet'
213 )
214 )
eabb27fd
JPC
215 response['Content-Disposition'] = \
216 'attachment; filename=Masse Salariale %s.ods' % \
217 masse.annee
218 return response
219 else:
220 c['rapport'] = masse.rapport
221 c['header_keys'] = [h[0] for h in masse.headers]
222 #on enleve le background pour le header
00ca4d9f
EMS
223 for h in (
224 h for h in masse.headers if 'background-color' in h[2]
225 ):
eabb27fd
JPC
226 h[2]['background'] = 'none'
227 h = SortHeaders(request, masse.headers, order_field_type="ot",
228 not_sortable=c['header_keys'], order_field="o")
229 c['headers'] = list(h.headers())
230 for key, nom, opts in masse.headers:
231 c['headers']
232 c['total'] = masse.grand_totaux[0]
233 c['total_euro'] = masse.grand_totaux[1]
234 c['colspan'] = len(c['header_keys']) - 1
235 get_filtre.append(('ods', True))
236 query_string = urllib.urlencode(get_filtre)
237 c['url_ods'] = "%s?%s" % (
238 reverse('rhr_masse_salariale'), query_string)
22b53270 239
890b80e7 240 return render(request, 'rh/rapports/masse_salariale.html', c)
98d6eb6c
JPC
241
242
243@login_required
244@drh_or_admin_required
6b7cf654 245def rapports_employes_sans_contrat(request):
1a7b9e36 246 lookup_params = get_lookup_params(request)
c8b22fd1 247
1a7b9e36
DB
248 # contrats échus
249 contrats_echus = rh.Contrat.objects.filter(
250 date_fin__lt=date.today()
251 ).filter(
252 **lookup_params
253 )
254
255 # dossiers en cours sans contrat
256 dossiers_sans_contrat = rh.Dossier.objects.filter(
257 Q(date_fin=None) | Q(date_fin__gt=date.today()),
258 ).exclude(
259 date_debut__gt=date.today()
260 ).filter(
261 rh_contrats__in=contrats_echus
262 )
10a11035 263
1a7b9e36 264 # employés sans contrat
10a11035
EMS
265 employes = rh.Employe.objects \
266 .filter(rh_dossiers__in=dossiers_sans_contrat).distinct()
c8b22fd1 267 if 'o' in request.GET:
1a7b9e36 268 employes = employes.order_by(
00ca4d9f
EMS
269 ('-' if request.GET.get('ot') == "desc" else '') + request.GET['o']
270 )
c8b22fd1 271
6b7cf654 272 # affichage
c8b22fd1 273 headers = [
dd18f6e3 274 ("id", u"#"),
1a7b9e36
DB
275 ("nom", u"Employé"),
276 ("dossier", u"Dossier : Poste"),
dd18f6e3
DB
277 ("dossier_date_debut", u"Début dossier"),
278 ("dossier_date_fin", u"Fin dossier"),
c8b22fd1 279 ]
00ca4d9f
EMS
280 h = SortHeaders(
281 request, headers, order_field_type="ot", order_field="o",
10a11035 282 not_sortable=('dossier', 'dossier_date_debut', 'dossier_date_fin')
00ca4d9f 283 )
c8b22fd1
JPC
284
285 c = {
286 'title': u'Rapport des employés sans contrat',
287 'employes': employes,
10a11035 288 'dossiers_sans_contrat': dossiers_sans_contrat,
c8b22fd1
JPC
289 'headers': list(h.headers()),
290 }
291
890b80e7 292 return render(request, 'rh/rapports/employes_sans_contrat.html', c)
00ca4d9f 293
c8b22fd1
JPC
294
295@login_required
296@drh_or_admin_required
857b5c24
JPC
297def rapports_postes_modelisation(request):
298 c = {}
299 data = []
3c1ba807 300
7bf28694
EMS
301 for categorie in rh.CategorieEmploi.objects.all():
302 types = rh.TypePoste.objects.filter(categorie_emploi=categorie)
3c1ba807
JPC
303 data_types = []
304 for t in types.all():
305 postes = rh.Poste.objects.filter(type_poste=t)
306 data_types.append({
307 'num_postes': postes.count(),
308 'postes': postes.all(),
7bf28694 309 'type': categorie,
857b5c24 310 })
3c1ba807 311
857b5c24 312 data.append({
7bf28694 313 'categorie': categorie,
3c1ba807 314 'nb_types': types.count(),
00ca4d9f
EMS
315 'types': data_types
316 })
857b5c24
JPC
317
318 c['data'] = data
319
890b80e7 320 return render(request, 'rh/rapports/postes_modelisation.html', c)
857b5c24
JPC
321
322
323@login_required
324@drh_or_admin_required
783e077a
JPC
325def rapports_postes_implantation(request):
326 c = {}
327 data = []
328 for r in ref.Region.objects.all():
329 implantations = []
330 for i in ref.Implantation.objects.filter(region=r):
331 implantations.append({
332 'implantation': i,
333 'postes': rh.Poste.objects.filter(implantation=i),
334 'num_postes': rh.Poste.objects.filter(implantation=i).count(),
335 })
336 data.append({
337 'region': r,
338 'implantations': implantations
339 })
340
341 c['data'] = data
342
890b80e7 343 return render(request, 'rh/rapports/postes_implantation.html', c)
783e077a
JPC
344
345
346@login_required
347@drh_or_admin_required
348def rapports_postes_service(request):
349 c = {}
9988aafb
JPC
350 data = []
351 for s in rh.Service.objects.all():
352 postes = rh.Poste.objects.filter(service=s).all()
353 num_postes = rh.Poste.objects.filter(service=s).count()
22343fe7 354 data.append({'service': s, 'num_postes': num_postes, 'postes': postes})
783e077a 355
9988aafb 356 c['data'] = data
890b80e7 357 return render(request, 'rh/rapports/postes_service.html', c)
32373f2e 358
10a11035 359
32373f2e 360@region_protected(rh.Dossier)
3ebc0952 361def dossier_apercu(request, dossier_id):
baf9b78c 362 d = get_object_or_404(rh.Dossier, pk=dossier_id)
3ebc0952 363 c = {
838bc59d 364 'title': u"Dossier %s" % (d, ),
00ca4d9f
EMS
365 'is_popup': request.GET.get('_popup', False),
366 'dossier': d,
367 'pieces': rh.DossierPiece.objects.filter(dossier__exact=d),
368 'contrats': rh.Contrat.objects.filter(dossier__exact=d),
369 'commentaires': rh.DossierCommentaire.objects.filter(dossier=d).all(),
abf91905 370 'media_url': settings.PRIVE_MEDIA_URL,
3ebc0952 371 }
890b80e7 372 return render(request, 'admin/rh/dossier/apercu.html', c)
00ca4d9f 373
4a1f2ece 374
fbe60a9d
OL
375@region_protected(rh.Poste)
376def poste_apercu(request, poste_id):
c8cb18e3 377 p = get_object_or_404(rh.Poste, pk=poste_id)
fbe60a9d 378 c = {
c8cb18e3 379 'title': u"Poste %s" % (p, ),
00ca4d9f
EMS
380 'is_popup': request.GET.get('_popup', False),
381 'poste': p,
382 'financements': (
383 rh.PosteFinancement.objects.filter(poste=poste_id).all()
384 ),
385 'pieces': rh.PostePiece.objects.filter(poste=poste_id).all(),
386 'dossiers': (
387 rh.Dossier.objects.filter(poste=poste_id)
388 .order_by("-date_debut").all()
389 ),
390 'comparaisons': (
391 rh.PosteComparaison.objects.filter(poste=poste_id).all()
392 ),
393 'commentaires': (
394 rh.PosteCommentaire.objects.filter(poste=poste_id).all()
395 ),
abf91905 396 'media_url': settings.PRIVE_MEDIA_URL,
fbe60a9d 397 }
890b80e7 398 return render(request, 'admin/rh/poste/apercu.html', c)
00ca4d9f 399
fbe60a9d 400
4a1f2ece
OL
401def employe_apercu(request, employe_id):
402 employe = get_object_or_404(rh.Employe, pk=employe_id)
fbe60a9d 403 user_groups = request.user.groups.all()
fbe60a9d
OL
404 dossiers = None
405
406 if request.user.is_superuser or \
407 grp_drh in user_groups:
408 q = Q(employe=employe)
409 if grp_correspondants_rh in user_groups:
00ca4d9f
EMS
410 regions = [
411 d.poste.implantation.region for d in employe.rh_dossiers.all()
412 ]
fbe60a9d 413 q = Q(employe=employe) & Q(implantation__region__in=regions)
22343fe7
OL
414
415 dossiers = rh.Dossier.objects.filter(q).order_by('-date_debut')
416
4a1f2ece 417 c = {
ce740bb5 418 'title': u"Employe %s" % (employe, ),
00ca4d9f
EMS
419 'is_popup': request.GET.get('_popup', False),
420 'employe': employe,
421 'dossiers': dossiers,
abf91905 422 'media_url': settings.PRIVE_MEDIA_URL,
4a1f2ece 423 }
890b80e7 424 return render(request, 'admin/rh/employe/apercu.html', c)
00ca4d9f 425
150d83ec 426
77bd83d1
JPC
427@login_required
428@drh_or_admin_required
56264a85 429def organigrammes_employe(request, id, level="all"):
08faf06e 430 poste = get_object_or_404(rh.Poste, pk=id)
00ca4d9f
EMS
431 dossiers_by_poste = dict(
432 (d.poste_id, d)
433 for d in rh.Dossier.objects.select_related('employe', 'poste').all()
434 )
5c0f1778
JPC
435 postes_by_id = dict((p.id, p) for p in rh.Poste.objects.all())
436
437 e = dossiers_by_poste[poste.id].employe
438 name = u"Organigramme de [%s] %s %s" % (e.id, e.nom.upper(), e.prenom)
58014aec 439 graph = pgv.AGraph()
5c0f1778 440
f187a10f 441 if rh.Poste.objects.filter(responsable=poste).count() > 0:
58014aec
JPC
442 postes_handle = [poste]
443 while postes_handle:
00ca4d9f
EMS
444 postes_handle = rh.Poste.objects.select_related('implantation') \
445 .filter(
446 Q(date_fin__gt=date.today()) | Q(date_fin=None),
447 Q(date_debut__lt=date.today()) | Q(date_debut=None),
448 responsable__in=postes_handle
449 ).exclude(supprime=True).exclude(responsable=None).all()
32373f2e 450
58014aec
JPC
451 for p in postes_handle:
452 if p.responsable_id != p.id:
00ca4d9f
EMS
453 graph.add_edge(
454 dossiers_by_poste[p.responsable_id].poste_id, p.id
455 )
13ad5ad5 456
f187a10f 457 else:
5c0f1778
JPC
458 graph.add_node(poste.id)
459
4afec2e1
JPC
460 if level != "all":
461 postes_niveau = [poste.id]
462 for niveau in range(int(level)):
00ca4d9f
EMS
463 postes_niveau = [
464 p.id for p in
465 rh.Poste.objects.filter(responsable__in=postes_niveau).all()
466 ]
4afec2e1
JPC
467
468 while postes_niveau:
00ca4d9f
EMS
469 postes_niveau = [
470 p.id for p in
471 rh.Poste.objects.filter(responsable__in=postes_niveau).all()
472 ]
4afec2e1
JPC
473 if postes_niveau:
474 for p in postes_niveau:
475 if graph.has_node(p):
4cc81304 476 graph.delete_node(p)
4afec2e1 477
58014aec 478 a = graph
4afec2e1 479 a.name = name.encode('ascii', 'xmlcharrefreplace')
5c0f1778
JPC
480
481 poste_remontant = poste
482 while poste_remontant.responsable_id:
483 a.add_edge(poste_remontant.responsable_id, poste_remontant.id)
484 poste_remontant = poste_remontant.responsable
485
486 rh_graph.bind_poste_to_graph(a, postes_by_id)
487 #a.graph_attr['normalize'] = True
488 #a.graph_attr['level'] = 2
489 a.layout(prog='dot')
490
491 svg = a.draw(format='svg')
492
493 c = {
494 'svg': svg
495 }
a251ac8d
JPC
496
497 if 'forcer' in request.GET:
890b80e7 498 response = HttpResponse(svg, content_type='image/svg+xml')
00ca4d9f
EMS
499 response['Content-Disposition'] = \
500 'attachment; filename=organigramme.svg'
a251ac8d
JPC
501 return response
502
890b80e7
DB
503 return render(request, 'rh/organigrammes/employe.html', c,
504 content_type="image/svg+xml"
00ca4d9f 505 )
5c0f1778
JPC
506
507
508@login_required
509@drh_or_admin_required
510def organigrammes_service(request, id):
5c0f1778 511 service = get_object_or_404(rh.Service, pk=id)
82af5c19
JPC
512 svg = rh_graph.organigramme_postes_cluster( \
513 cluster_filter={"service": service}, \
514 titre=u"Organigramme du service %s" % service.nom,
515 cluster_titre=service.nom)
5c0f1778 516
82af5c19
JPC
517 c = {
518 'svg': svg
519 }
5c0f1778 520
10a11035 521 return render(request, 'rh/organigrammes/vide.html', c,
890b80e7 522 content_type="image/svg+xml"
00ca4d9f
EMS
523 )
524
5c0f1778 525
82af5c19
JPC
526@login_required
527@drh_or_admin_required
528def organigrammes_implantation(request, id):
82af5c19
JPC
529 implantation = get_object_or_404(ref.Implantation, pk=id)
530 svg = rh_graph.organigramme_postes_cluster( \
531 cluster_filter={"implantation": implantation}, \
532 titre=u"Organigramme de l'implantation %s" % implantation.nom,
533 cluster_titre=implantation.nom)
5c0f1778
JPC
534
535 c = {
536 'svg': svg
537 }
538
10a11035 539 return render(request, 'rh/organigrammes/vide.html', c,
890b80e7 540 content_type="image/svg+xml"
00ca4d9f
EMS
541 )
542
5c0f1778 543
9da4c195
JPC
544@login_required
545@drh_or_admin_required
546def organigrammes_region(request, id):
9da4c195
JPC
547 region = get_object_or_404(ref.Region, pk=id)
548 svg = rh_graph.organigramme_postes_cluster( \
549 cluster_filter={"implantation__region": region}, \
550 titre=u"Organigramme du bureau de %s" % region.nom,
551 cluster_titre=region.nom)
552
553 c = {
554 'svg': svg
555 }
556
10a11035
EMS
557 return render(request,
558 'rh/organigrammes/vide.html', c,
890b80e7 559 content_type="image/svg+xml"
00ca4d9f 560 )