Refactoring, supression de networkx
[auf_rh_dae.git] / project / rh / views.py
1 # -*- encoding: utf-8 -*-
2
3 from datetime import date
4 from itertools import izip
5 import pygraphviz as pgv
6
7 from django.db.models import Q
8 from django.contrib.auth.decorators import login_required
9 from django.utils.encoding import smart_str
10 from django.shortcuts import render_to_response, get_object_or_404
11 from django.template import RequestContext
12 from django.http import HttpResponse
13
14 from datamaster_modeles import models as ref
15
16 from rh import models as rh
17 from rh.lib import calc_remun
18 from rh.decorators import drh_or_admin_required
19 from rh.templatetags.rapports import SortHeaders
20 from rh.change_list import RechercheTemporelle
21 from rh import graph as rh_graph
22
23 # pas de reference a DAE devrait etre refactorisé
24 from dae.utils import get_employe_from_user
25 from dae.decorators import redirect_interdiction
26 from dae.workflow import grp_drh, grp_correspondants_rh
27 from django.conf import settings
28
29
30 @login_required
31 def profil(request):
32 """Profil personnel de l'employé - éditable"""
33 rc = RequestContext(request)
34 c = {}
35
36 employe = rc['this_employe']
37
38 c['user'] = request.user
39 c['employe'] = employe
40 return render_to_response('rh/profil.html', c, rc)
41
42
43 @login_required
44 def employes_liste(request):
45 """Liste des employés."""
46 today = date.today()
47 employes = rh.Employe.objects \
48 .exclude(dossiers__date_debut__gt=today) \
49 .exclude(dossiers__date_fin__lt=today) \
50 .order_by('nom')
51 c = {
52 'user': request.user,
53 'employes': employes,
54 }
55 return render_to_response('rh/employes_liste.html',
56 c,
57 RequestContext(request))
58
59
60 @login_required
61 def employe(request, id):
62 """Information publique sur un employé."""
63 try:
64 employe = rh.Employe.objects.get(pk=id)
65 except:
66 employe = rh.Employe.objects.none()
67 c = {
68 'user': request.user,
69 'employe': employe,
70 }
71 return render_to_response('rh/employe.html', c, RequestContext(request))
72
73
74 @login_required
75 @drh_or_admin_required
76 def rapports_poste(request):
77
78 lookup_params = dict(request.GET.items())
79
80 for key, value in lookup_params.items():
81 if key == 'o' or key == 'ot':
82 del lookup_params[key]
83 continue
84 if not isinstance(key, str):
85 # 'key' will be used as a keyword argument later, so Python
86 # requires it to be a string.
87 del lookup_params[key]
88 lookup_params[smart_str(key)] = value
89
90 if key == 'comble':
91 del lookup_params[key]
92
93 postes = rh.Poste.objects.select_related('implantation') \
94 .extra(select={'employe_id':'select group_concat(employe separator "|") from rh_dossier where poste=rh_poste.id and rh_dossier.date_fin is null %s' % \
95 ("%s" % (('order by employe %s' % ('asc' if 'ot' in request.GET and request.GET['ot'] == "asc" else "desc"))) if 'o' in request.GET and request.GET['o'] == "employe_id" else "") \
96 }) \
97 .extra(select={'employe_nom':'select group_concat(rh_employe.nom separator "|") from rh_dossier inner join rh_employe on rh_dossier.employe = rh_employe.id where poste=rh_poste.id and rh_dossier.date_fin is null %s' % \
98 ("%s" % (('order by rh_employe.nom %s' % ('asc' if 'ot' in request.GET and request.GET['ot'] == "asc" else "desc"))) if 'o' in request.GET and request.GET['o'] == "employe_nom" else "") \
99 }) \
100 .extra(select={'employe_prenom':'select group_concat(rh_employe.prenom separator "|") from rh_dossier inner join rh_employe on rh_dossier.employe = rh_employe.id where poste=rh_poste.id and rh_dossier.date_fin is null %s' % \
101 ("%s" % (('order by rh_employe.prenom %s' % ('asc' if 'ot' in request.GET and request.GET['ot'] == "asc" else "desc"))) if 'o' in request.GET and request.GET['o'] == "employe_prenom" else "") \
102 })
103
104 postes = postes.filter(**lookup_params)
105 if 'o' in request.GET:
106 postes = postes.order_by("%s%s" % ('-' if 'ot' in request.GET and request.GET['ot'] == "desc" else '', request.GET['o']))
107
108 out = []
109 for p in postes:
110 out.append({
111 'id': p.id,
112 'nom': p.nom,
113 'implantation': p.implantation,
114 'employes': [] if not p.employe_id else [{'id': id, 'nom': nom, 'prenom': prenom} for id, nom, prenom in izip(p.employe_id.split('|'), p.employe_nom.split('|'), p.employe_prenom.split('|'))] })
115
116
117 headers = [
118 ("id", u"# du poste"),
119 ("nom", u"Nom du poste"),
120 ("implantation__id", u"Implantation"),
121 ("employe_nom", u"Nom"),
122 ]
123 h = SortHeaders(request, headers, order_field_type="ot", order_field="o")
124 c = {
125 'title': 'Rapport des postes',
126 'postes': out,
127 'count': len(out),
128 'headers': list(h.headers()),
129 }
130
131 return render_to_response('rh/rapports/postes.html', c, RequestContext(request))
132
133 @login_required
134 @drh_or_admin_required
135 def rapports_contrat(request):
136
137 lookup_params = dict(request.GET.items())
138 if 'ot' in lookup_params:
139 del lookup_params['ot']
140 if 'o' in lookup_params:
141 del lookup_params['o']
142
143 for key, value in lookup_params.items():
144 if not isinstance(key, str):
145 # 'key' will be used as a keyword argument later, so Python
146 # requires it to be a string.
147 del lookup_params[key]
148 lookup_params[smart_str(key)] = value
149
150 contrats = rh.Contrat.objects.select_related('dossier', 'dossier__poste', 'dossier__poste__implantation', 'type_contrat', 'dossier__employe')
151
152 cl = RechercheTemporelle(request, rh.Contrat)
153 lookup_params = cl.purge_params(lookup_params)
154 contrats = contrats.filter(**lookup_params).exclude(dossier__employe__supprime=1)
155 contrats = cl.filter_temporel(contrats)
156
157 if 'o' in request.GET:
158 contrats = contrats.order_by("%s%s" % ('-' if 'ot' in request.GET and request.GET['ot'] == "desc" else '', request.GET['o']))
159
160 employes = set([c.dossier.employe_id for c in contrats])
161
162 headers = [
163 ("dossier__employe__id", u"# de l'employé"),
164 ("dossier__employe__nom", u"Nom"),
165 ("dossier__employe__prenom", u"Prénom"),
166 ("type_contrat__nom", u"Poste"),
167 ("type_contrat__nom", u"Type de contrat"),
168 ("date_debut", u"Date début"),
169 ("date_fin", u"Date fin"),
170 ]
171 h = SortHeaders(request, headers, order_field_type="ot", order_field="o")
172
173 c = {
174 'cl' : cl,
175 'title': 'Rapport des contrats',
176 'contrats': contrats,
177 'count': len(contrats),
178 'count_employe' : len(employes),
179 'headers': list(h.headers()),
180 }
181
182 return render_to_response('rh/rapports/contrats.html', c, RequestContext(request))
183
184
185 @login_required
186 @drh_or_admin_required
187 def rapports_remuneration(request):
188
189 lookup_params = dict(request.GET.items())
190 if 'ot' in lookup_params:
191 del lookup_params['ot']
192 if 'o' in lookup_params:
193 del lookup_params['o']
194
195 for key, value in lookup_params.items():
196 if not isinstance(key, str):
197 # 'key' will be used as a keyword argument later, so Python
198 # requires it to be a string.
199 del lookup_params[key]
200 lookup_params[smart_str(key)] = value
201
202 employes = rh.Employe.objects.all()
203 if 'o' in request.GET:
204 employes = employes.order_by("%s%s" % ('-' if 'ot' in request.GET and request.GET['ot'] == "desc" else '', request.GET['o']))
205
206 employes = employes.filter(**lookup_params)
207
208 output = []
209 headers = [
210 ("id", u"# de l'employé"),
211 ("nom", u"Nom"),
212 ("prenom", u"Prénom"),
213 ("", u"Salaire"),
214 ("", u"RAS"),
215 ("", u"Indemnités"),
216 ("", u"Accessoire"),
217 ("", u"Charges patronales"),
218 ("", u"Total"),
219 ]
220 h = SortHeaders(request, headers, order_field_type="ot", order_field="o")
221
222 for employe in employes:
223 line = {}
224 output.append(line)
225
226 dossiers = employe.rh_dossiers.all()
227
228 remun = {}
229 remun_sum_euro = 0
230
231 for dossier in dossiers:
232 this_remun, this_remun_sum, this_remun_sum_euro = calc_remun(dossier)
233
234 for item in this_remun:
235 if item not in remun:
236 remun[item] = this_remun[item]
237 else:
238 remun[item][0] += this_remun[item][0]
239 remun[item][1] += this_remun[item][1]
240
241 remun_sum_euro += this_remun_sum_euro
242
243 line['remun_sum_euro'] = remun_sum_euro
244
245 for r in remun:
246 if r == u'Indemnité':
247 line['Indemnite'] = remun[r][1]
248 else:
249 line[r] = remun[r][1]
250
251 line['id'] = employe.id
252 line['nom'] = employe.nom
253 line['prenom'] = employe.prenom
254
255
256 c = {
257 'title': 'Rapport de remuneration',
258 'employes': output,
259 'headers': list(h.headers()),
260 }
261
262 return render_to_response('rh/rapports/remuneration.html', c, RequestContext(request))
263
264 @login_required
265 @drh_or_admin_required
266 def rapports_employe_sans_contrat(request):
267
268 lookup_params = dict(request.GET.items())
269 if 'ot' in lookup_params:
270 del lookup_params['ot']
271 if 'o' in lookup_params:
272 del lookup_params['o']
273
274 for key, value in lookup_params.items():
275 if not isinstance(key, str):
276 # 'key' will be used as a keyword argument later, so Python
277 # requires it to be a string.
278 del lookup_params[key]
279 lookup_params[smart_str(key)] = value
280
281 employes_query = rh.Employe.objects
282 if 'o' in request.GET:
283 employes_query = employes_query.order_by("%s%s" % ('-' if 'ot' in request.GET and request.GET['ot'] == "desc" else '', request.GET['o']))
284
285 employes = {}
286
287 dossiers_en_cours = rh.Dossier.objects.filter(Q(date_fin=None) | Q(date_fin__gt=date.today()))
288 tous_contrats_echus = rh.Contrat.objects.filter(date_fin__lt=date.today(), dossier__in=dossiers_en_cours)
289 contrats = tous_contrats_echus.filter(**lookup_params).all()
290 for c in contrats:
291 if c.dossier.employe.id not in employes.keys():
292 employes[c.dossier.employe.id] = {'employe': c.dossier.employe, 'dossiers': []}
293 employes[c.dossier.employe.id]['dossiers'] += [c.dossier,]
294
295
296
297 headers = [
298 ("id", u"# de l'employé"),
299 ("nom", u"Nom"),
300 ("prenom", u"Prénom"),
301 ("dossier", u"Dossiers"),
302 ]
303 h = SortHeaders(request, headers, order_field_type="ot", order_field="o", not_sortable=('dossier',))
304
305 c = {
306 'title': u'Rapport des employés sans contrat',
307 'employes': employes,
308 'count': len(employes),
309 'headers': list(h.headers()),
310 }
311
312 return render_to_response('rh/rapports/employes_sans_contrat.html', c, RequestContext(request))
313
314 @login_required
315 @drh_or_admin_required
316 def rapports_postes_modelisation(request):
317 c = {}
318 data = []
319
320 for f in rh.FamilleEmploi.objects.all():
321 types = rh.TypePoste.objects.filter(famille_emploi=f)
322 data_types = []
323 for t in types.all():
324 postes = rh.Poste.objects.filter(type_poste=t)
325 data_types.append({
326 'num_postes': postes.count(),
327 'postes': postes.all(),
328 'type': f,
329 })
330
331 data.append({
332 'famille': f,
333 'nb_types': types.count(),
334 'types' : data_types
335 })
336
337 c['data'] = data
338
339
340 return render_to_response('rh/rapports/postes_modelisation.html', c, RequestContext(request))
341
342
343 @login_required
344 @drh_or_admin_required
345 def rapports_postes_implantation(request):
346 c = {}
347 data = []
348 for r in ref.Region.objects.all():
349 implantations = []
350 for i in ref.Implantation.objects.filter(region=r):
351 implantations.append({
352 'implantation': i,
353 'postes': rh.Poste.objects.filter(implantation=i),
354 'num_postes': rh.Poste.objects.filter(implantation=i).count(),
355 })
356 data.append({
357 'region': r,
358 'implantations': implantations
359 })
360
361 c['data'] = data
362
363
364 return render_to_response('rh/rapports/postes_implantation.html', c, RequestContext(request))
365
366
367 @login_required
368 @drh_or_admin_required
369 def rapports_postes_service(request):
370 c = {}
371 data = []
372 for s in rh.Service.objects.all():
373 postes = rh.Poste.objects.filter(service=s).all()
374 num_postes = rh.Poste.objects.filter(service=s).count()
375 data.append({'service': s, 'num_postes': num_postes, 'postes': postes})
376
377 c['data'] = data
378 return render_to_response('rh/rapports/postes_service.html', c, RequestContext(request))
379
380
381 def region_protected(model):
382 def wrapper(func):
383 def wrapped(request, id):
384 if request.user.is_superuser:
385 return func(request, id)
386 user_groups = request.user.groups.all()
387 if grp_drh in user_groups:
388 return func(request, id)
389 if grp_correspondants_rh in user_groups:
390 employe = get_employe_from_user(request.user)
391 q = Q(**{model.prefix_implantation: employe.implantation.region})
392 qs = model.objects.filter(q)
393 if int(id) in [o.id for o in qs]:
394 return func(request, id)
395 return redirect_interdiction(request)
396 return wrapped
397 return wrapper
398
399
400 @region_protected(rh.Dossier)
401 def dossier_apercu(request, dossier_id):
402 d = get_object_or_404(rh.Dossier, pk=dossier_id)
403 c = {
404 'is_popup' : request.GET.get('_popup', False),
405 'dossier' : d,
406 'pieces' : rh.DossierPiece.objects.filter(dossier__exact=d),
407 'contrats' : rh.Contrat.objects.filter(dossier__exact=d),
408 'commentaires' : rh.DossierCommentaire.objects.filter(dossier__exact=d).all(),
409 'media_url': settings.PRIVE_MEDIA_URL,
410 }
411 return render_to_response('admin/rh/dossier/apercu.html', c, RequestContext(request))
412
413 @region_protected(rh.Poste)
414 def poste_apercu(request, poste_id):
415 c = {
416 'is_popup' : request.GET.get('_popup', False),
417 'poste' : get_object_or_404(rh.Poste, pk=poste_id),
418 'financements' : rh.PosteFinancement.objects.filter(poste=poste_id).all(),
419 'pieces' : rh.PostePiece.objects.filter(poste=poste_id).all(),
420 'dossiers' : rh.Dossier.objects.filter(poste=poste_id).order_by("-date_debut").all(),
421 'comparaisons' : rh.PosteComparaison.objects.filter(poste=poste_id).all(),
422 'commentaires' : rh.PosteCommentaire.objects.filter(poste=poste_id).all(),
423 'media_url': settings.PRIVE_MEDIA_URL,
424 }
425 return render_to_response('admin/rh/poste/apercu.html', c, RequestContext(request))
426
427 def employe_apercu(request, employe_id):
428 employe = get_object_or_404(rh.Employe, pk=employe_id)
429 user_groups = request.user.groups.all()
430 dossiers = None
431
432 if request.user.is_superuser or \
433 grp_drh in user_groups:
434 q = Q(employe=employe)
435 if grp_correspondants_rh in user_groups:
436 regions = [d.poste.implantation.region for d in employe.rh_dossiers.all()]
437 q = Q(employe=employe) & Q(implantation__region__in=regions)
438
439 dossiers = rh.Dossier.objects.filter(q).order_by('-date_debut')
440
441 c = {
442 'is_popup' : request.GET.get('_popup', False),
443 'employe' : employe,
444 'dossiers' : dossiers,
445 'media_url': settings.PRIVE_MEDIA_URL,
446 }
447 return render_to_response('admin/rh/employe/apercu.html', c, RequestContext(request))
448
449 @login_required
450 @drh_or_admin_required
451 def organigrammes_employe(request, id, level="all"):
452
453 poste = get_object_or_404(rh.Poste, pk=id)
454 dossiers_by_poste = dict((d.poste_id, d) for d in rh.Dossier.objects.select_related('employe', 'poste').all())
455 postes_by_id = dict((p.id, p) for p in rh.Poste.objects.all())
456
457 e = dossiers_by_poste[poste.id].employe
458 name = u"Organigramme de [%s] %s %s" % (e.id, e.nom.upper(), e.prenom)
459 graph = pgv.AGraph()
460
461 if rh.Poste.objects.filter(responsable=poste).count() > 0:
462 postes_handle = [poste]
463 while postes_handle:
464 postes_handle = rh.Poste.objects.select_related('implantation').filter((Q(date_fin__gt=date.today()) | Q(date_fin=None)) & (Q(date_debut__lt=date.today()) | Q(date_debut=None)) ).filter(responsable__in=postes_handle).exclude(supprime=True).exclude(responsable=None).all()
465
466 for p in postes_handle:
467 if p.responsable_id != p.id:
468 graph.add_edge(dossiers_by_poste[p.responsable_id].poste_id, p.id)
469
470 else:
471 graph.add_node(poste.id)
472
473 if level != "all":
474 postes_niveau = [poste.id]
475 for niveau in range(int(level)):
476 postes_niveau = [p.id for p in rh.Poste.objects.filter(responsable__in=postes_niveau).all()]
477
478 while postes_niveau:
479 postes_niveau = [p.id for p in rh.Poste.objects.filter(responsable__in=postes_niveau).all()]
480 if postes_niveau:
481 for p in postes_niveau:
482 if graph.has_node(p):
483 graph.remove_node(p)
484
485 a = graph
486 a.name = name.encode('ascii', 'xmlcharrefreplace')
487
488 poste_remontant = poste
489 while poste_remontant.responsable_id:
490 a.add_edge(poste_remontant.responsable_id, poste_remontant.id)
491 poste_remontant = poste_remontant.responsable
492
493 rh_graph.bind_poste_to_graph(a, postes_by_id)
494 #a.graph_attr['normalize'] = True
495 #a.graph_attr['level'] = 2
496 a.layout(prog='dot')
497
498 svg = a.draw(format='svg')
499
500 c = {
501 'svg': svg
502 }
503
504 if 'forcer' in request.GET:
505 response = HttpResponse(svg, mimetype='image/svg+xml')
506 response['Content-Disposition'] = 'attachment; filename=organigramme.svg'
507 return response
508
509 return render_to_response('rh/organigrammes/employe.html', c, RequestContext(request), mimetype="image/svg+xml")
510
511
512 @login_required
513 @drh_or_admin_required
514 def organigrammes_service(request, id):
515
516 service = get_object_or_404(rh.Service, pk=id)
517 svg = rh_graph.organigramme_postes_cluster( \
518 cluster_filter={"service": service}, \
519 titre=u"Organigramme du service %s" % service.nom,
520 cluster_titre=service.nom)
521
522 c = {
523 'svg': svg
524 }
525
526 return render_to_response('rh/organigrammes/vide.html', c, RequestContext(request), mimetype="image/svg+xml")
527
528 @login_required
529 @drh_or_admin_required
530 def organigrammes_implantation(request, id):
531
532 implantation = get_object_or_404(ref.Implantation, pk=id)
533 svg = rh_graph.organigramme_postes_cluster( \
534 cluster_filter={"implantation": implantation}, \
535 titre=u"Organigramme de l'implantation %s" % implantation.nom,
536 cluster_titre=implantation.nom)
537
538 c = {
539 'svg': svg
540 }
541
542 return render_to_response('rh/organigrammes/vide.html', c, RequestContext(request), mimetype="image/svg+xml")
543
544 @login_required
545 @drh_or_admin_required
546 def organigrammes_region(request, id):
547
548 region = get_object_or_404(ref.Region, pk=id)
549 svg = rh_graph.organigramme_postes_cluster( \
550 cluster_filter={"implantation__region": region}, \
551 titre=u"Organigramme du bureau de %s" % region.nom,
552 cluster_titre=region.nom)
553
554 c = {
555 'svg': svg
556 }
557
558 return render_to_response('rh/organigrammes/vide.html', c, RequestContext(request), mimetype="image/svg+xml")
559