| 1 | # -*- encoding: utf-8 -*- |
| 2 | |
| 3 | from datetime import date |
| 4 | |
| 5 | from django.db.models import Q |
| 6 | from django.core.urlresolvers import reverse |
| 7 | import pygraphviz as pgv |
| 8 | |
| 9 | from project.permissions import user_can_change_obj |
| 10 | from project.rh import models as rh |
| 11 | |
| 12 | |
| 13 | def bind_poste_to_graph(user, graph, postes_by_id): |
| 14 | for n in graph.nodes(): |
| 15 | # poste = node |
| 16 | poste = postes_by_id[int(n)] |
| 17 | |
| 18 | # dossiers actifs pour ce poste |
| 19 | dossiers = rh.Dossier.objects.select_related('employe').filter( |
| 20 | (Q(date_fin__gt=date.today()) | Q(date_fin=None)) & |
| 21 | (Q(date_debut__lt=date.today()) | Q(date_debut=None)) & |
| 22 | Q(poste__id=n) |
| 23 | ).all() |
| 24 | |
| 25 | # affichage |
| 26 | if dossiers: |
| 27 | employes = "\\n".join( |
| 28 | ["[%s] %s %s" % |
| 29 | (d.employe_id, d.employe.nom.upper(), |
| 30 | d.employe.prenom |
| 31 | ) for d in dossiers] |
| 32 | ) |
| 33 | label = u"%s\\n%s\\n%s" % ( |
| 34 | d.poste.nom, employes, d.poste.implantation |
| 35 | ) |
| 36 | else: |
| 37 | label = u"%s\\n---\\n%s" % (poste.nom, poste.implantation) |
| 38 | n.attr['fillcolor'] = 'azure4' |
| 39 | n.attr['style'] = 'filled' |
| 40 | |
| 41 | n.attr['label'] = label.encode('ascii', 'xmlcharrefreplace') |
| 42 | # lien seulement si user peut editer poste |
| 43 | if user_can_change_obj(user, poste): |
| 44 | n.attr['href'] = reverse("admin:rh_poste_change", args=(n,)) |
| 45 | |
| 46 | return graph |
| 47 | |
| 48 | |
| 49 | def organigramme_postes_cluster(user, cluster_filter, titre=u"Organigramme", |
| 50 | cluster_titre=u"Cluster 1"): |
| 51 | """ |
| 52 | Crée un organigramme des postes avec un cluster défini par le keyword |
| 53 | qui sera rajouté au queryset en tant que filter. |
| 54 | |
| 55 | cluster_filter doit être un map de format (field: value) qui sera |
| 56 | appliqué au queryset. |
| 57 | |
| 58 | Par exemple: cluster_filter={"service__exact": 19} |
| 59 | """ |
| 60 | if type(titre) != type(unicode()): |
| 61 | raise "Le titre du graphique doit être un unicode" |
| 62 | if type(cluster_titre) != type(unicode()): |
| 63 | raise "Le titre du cluster doit être un unicode" |
| 64 | |
| 65 | postes_by_id = dict((p.id, p) for p in rh.Poste.objects.all()) |
| 66 | |
| 67 | postes = rh.Poste.objects.select_related('implantation').filter( |
| 68 | Q(date_fin__gt=date.today()) | Q(date_fin=None), |
| 69 | Q(date_debut__lt=date.today()) | Q(date_debut=None), |
| 70 | **cluster_filter |
| 71 | ).exclude(responsable=None) |
| 72 | |
| 73 | nom = titre.encode('ascii', 'xmlcharrefreplace') |
| 74 | graph = pgv.AGraph(directed=True, name=nom) |
| 75 | #pour mettre l'organigramme de gauche à droite (au lieu du haut à bas) |
| 76 | #graph.rankdir='LR' |
| 77 | graph_service = graph.subgraph(nbunch=[1, 2], name="cluster1") |
| 78 | graph_service.graph_attr['style'] = 'filled' |
| 79 | graph_service.graph_attr['color'] = 'lightgrey' |
| 80 | graph_service.graph_attr['label'] = \ |
| 81 | cluster_titre.encode('ascii', 'xmlcharrefreplace') |
| 82 | graph_service.graph_attr['labeljust'] = 'l' |
| 83 | |
| 84 | for p in postes: |
| 85 | graph_service.add_node(p.id) |
| 86 | if p.responsable_id: |
| 87 | graph.add_edge(p.responsable_id, p.id) |
| 88 | |
| 89 | for p_id in graph_service.nodes(): |
| 90 | if postes_by_id[int(p_id)].responsable_id: |
| 91 | poste_remontant = postes_by_id[int(p_id)] |
| 92 | while poste_remontant.responsable_id \ |
| 93 | and poste_remontant.responsable_id \ |
| 94 | and poste_remontant.responsable_id != poste_remontant.id: |
| 95 | poste_remontant = postes_by_id[poste_remontant.responsable_id] |
| 96 | if poste_remontant.responsable_id: |
| 97 | graph.add_edge( |
| 98 | poste_remontant.responsable_id, poste_remontant.id |
| 99 | ) |
| 100 | |
| 101 | bind_poste_to_graph(user, graph, postes_by_id) |
| 102 | |
| 103 | graph.layout(prog='dot') |
| 104 | |
| 105 | svg = graph.draw(format='svg') |
| 106 | |
| 107 | return svg |