Organigramme de poste
authorJean-Philippe Caissy <jpcaissy@piji.ca>
Wed, 29 Feb 2012 22:27:29 +0000 (16:27 -0600)
committerJean-Philippe Caissy <jpcaissy@piji.ca>
Wed, 29 Feb 2012 22:27:29 +0000 (16:27 -0600)
project/menu.py
project/rh/admin.py
project/rh/models.py
project/rh/templates/admin/rh/employeproxy/change_list.html [new file with mode: 0644]
project/rh/templates/rh/organigrammes/employe.html [new file with mode: 0644]
project/rh/urls.py
project/rh/views.py

index 96572e8..1a1ab8e 100644 (file)
@@ -52,6 +52,10 @@ class CustomMenu(Menu):
                                    #items.MenuItem('Rapport hiérarchique des postes', reverse('rhr_postes_hierarchie')),
                                ]
                               ),
+                items.MenuItem('Organigrammes',
+                    children=[
+                        ]
+                    ),
             ]
         super(CustomMenu, self).init_with_context(context)
         
index 33d79be..8735c2d 100644 (file)
@@ -465,6 +465,31 @@ class EmployeAdmin(DateRangeMixin, AUFMetadataAdminMixin, ProtectRegionMixin, ad
                 instance.date_creation = datetime.datetime.now()
             instance.save()
 
+class EmployeProxyAdmin(EmployeAdmin):
+    list_display = ('_id', '_apercu', '_nom', '_organigramme') 
+    list_display_links = ('_nom',)
+
+    def _organigramme(self, obj):
+        l = []
+        for d in obj.rh_dossiers.all().order_by('-date_debut'):
+            poste = u"""<a title="Aperçu du poste" href="%s" onclick="return showAddAnotherPopup(this);" title="Aperçu du poste"><img src="%simg/loupe.png" /></a><a href="%s">Poste</a>&nbsp;""" % \
+                                 (  reverse('poste_apercu', args=(d.poste.id,)),
+                                    settings.STATIC_URL,
+                                    reverse('admin:rh_poste_change', args=(d.poste.id,))
+                                 )
+            organigramme = u"""<a href="%s">Organigramme</a>""" % (reverse('rho_employe', args=(d.poste.id,)))
+            link = u"""<li>%s %s - %s : [%s] %s</li>""" % \
+                (poste, organigramme,
+                 d.date_debut.year,
+                 d.poste.id,
+                 d.poste.nom,
+                 )
+            l.append(link)
+        return "<ul>%s</ul>" % "\n".join(l)
+
+    _organigramme.allow_tags = True
+    _organigramme.short_description = "Organigramme"
+
 
 
 class EmployeCommentaireAdmin(admin.ModelAdmin):
@@ -855,6 +880,7 @@ class ValeurPointAdmin(AUFMetadataAdminMixin, admin.ModelAdmin):
 admin.site.register(rh.Classement, ClassementAdmin)
 admin.site.register(rh.Devise, DeviseAdmin)
 admin.site.register(rh.Dossier, DossierAdmin)
+admin.site.register(rh.EmployeProxy, EmployeProxyAdmin)
 admin.site.register(rh.Employe, EmployeAdmin)
 admin.site.register(rh.FamilleEmploi, FamilleEmploiAdmin)
 admin.site.register(rh.OrganismeBstg, OrganismeBstgAdmin)
index 0ada264..12f13aa 100644 (file)
@@ -8,6 +8,7 @@ from django.db.models import signals
 from django.core.files.storage import FileSystemStorage
 from django.db import models
 from django.conf import settings
+from django.db.models import Q
 
 from auf.django.emploi.models import GENRE_CHOICES, SITUATION_CHOICES # devrait plutot être dans references
 from auf.django.metadata.models import AUFMetadata
@@ -197,9 +198,9 @@ class Poste_(AUFMetadata):
     def __unicode__(self):
         representation = u'%s - %s [%s]' % (self.implantation, self.nom,
                             self.id)
+        return "%s" % self.id
         return representation
 
-
     prefix_implantation = "implantation__region"
     def get_regions(self):
         return [self.implantation.region]
@@ -473,6 +474,12 @@ class EmployeCommentaire(Commentaire):
         verbose_name = u"Employé commentaire"
         verbose_name_plural = u"Employé commentaires"
 
+class EmployeProxy(Employe):
+
+    class Meta:
+        proxy = True
+        verbose_name = u"Organigramme des employés"
+        verbose_name_plural = u"Organigramme des employés"
 
 LIEN_PARENTE_CHOICES = (
     ('Conjoint', 'Conjoint'),
@@ -640,7 +647,6 @@ class Dossier_(AUFMetadata):
         except:
             return None
 
-
 class Dossier(Dossier_):
     __doc__ = Dossier_.__doc__
     poste = models.ForeignKey('%s.Poste' % app_context(),
@@ -658,7 +664,7 @@ class DossierPiece_(models.Model):
     """Documents relatifs au Dossier (à l'occupation de ce poste par employé).
     Ex.: Lettre de motivation.
     """
-    dossier = models.ForeignKey('%s.Dossier' % app_context(), db_column='dossier', related_name='%(app_label)s_dossierpieces')
+    dossier = models.ForeignKey('%s.Dossier' % app_context(), db_column='dossier', related_name='+')
     nom = models.CharField(verbose_name = u"Nom", max_length=255)
     fichier = models.FileField(verbose_name = u"Fichier",
                             upload_to=dossier_piece_dispatch,
@@ -699,7 +705,7 @@ class DossierComparaison_(models.Model):
         abstract = True
 
     def taux_devise(self):
-        annee = self.dossier.contrat_date_debut.year
+        annee = self.dossier.poste.date_debut.year
         taux = [tc.taux for tc in TauxChange.objects.filter(devise=self.devise, annee=annee)]
         taux = set(taux)
         if len(taux) != 1:
@@ -759,17 +765,15 @@ class Remuneration_(RemunerationMixin):
             return 1
 
         annee = datetime.datetime.now().year
-        if self.dossier.poste.date_debut is not None:
-            annee = self.dossier.poste.date_debut.year
-        if self.dossier.date_debut is not None:
-            annee = self.dossier.date_debut.year
         if self.date_debut is not None:
             annee = self.date_debut.year
+        if self.dossier.poste.date_debut is not None:
+            annee = self.dossier.poste.date_debut.year
 
         taux = [tc.taux for tc in TauxChange.objects.filter(devise=self.devise_id, annee=annee)]
         taux = set(taux)
         if len(taux) != 1:
-            raise Exception(u"Le taux de la devise %s n'a pas n'existe pas pour %s ou il existe plusieurs taux pour la même année %s (%s)" % (self.devise.code, annee, taux, self.dossier))
+            raise Exception(u"Le taux de la devise %s n'a pas n'existe pas pour %s ou il existe plusieurs taux pour la même année" % (self.devise.id, annee))
         else:
             return list(taux)[0]
 
@@ -1099,7 +1103,8 @@ class Classement_(AUFMetadata):
         verbose_name_plural = u"Classements"
 
     def __unicode__(self):
-        return u'%s.%s.%s' % (self.type, self.echelon, self.degre, )
+        return u'%s.%s.%s (%s)' % (self.type, self.echelon, self.degre,
+                                   self.coefficient)
 
 class Classement(Classement_):
     __doc__ = Classement_.__doc__
diff --git a/project/rh/templates/admin/rh/employeproxy/change_list.html b/project/rh/templates/admin/rh/employeproxy/change_list.html
new file mode 100644 (file)
index 0000000..b0b747f
--- /dev/null
@@ -0,0 +1,7 @@
+{% extends "alphafilter/change_list.html" %}
+{% load change_list %}
+
+{% block search %}
+    {{ block.super }}
+    {% recherche_par_annees cl %}
+{% endblock %}
diff --git a/project/rh/templates/rh/organigrammes/employe.html b/project/rh/templates/rh/organigrammes/employe.html
new file mode 100644 (file)
index 0000000..840b13f
--- /dev/null
@@ -0,0 +1 @@
+<img src="data:image/png;base64,{{ png }}" />
index 4479d29..48aedd2 100644 (file)
@@ -15,4 +15,5 @@ urlpatterns = patterns(
     url(r'^admin/rh/dossier/(\d+)/apercu/$', 'dossier_apercu', name='dossier_apercu'),
     url(r'^admin/rh/employe/(\d+)/apercu/$', 'employe_apercu', name='employe_apercu'),
     url(r'^admin/rh/poste/(\d+)/apercu/$', 'poste_apercu', name='poste_apercu'),
+    url(r'^admin/rh/organigrammes/employe/(\d+)$', 'organigrammes_employe', name='rho_employe'),
 )
index 27dc661..bb660ae 100644 (file)
@@ -1,6 +1,7 @@
 # -*- encoding: utf-8 -*-
 from datetime import date
 from itertools import izip
+from base64 import b64encode
 import networkx as nx
 
 from django.db.models import Q
@@ -89,6 +90,11 @@ def rapports_postes_hierarchie(request):
 
 @login_required
 @drh_or_admin_required
+def hierarchie_poste(request):
+    pass
+
+@login_required
+@drh_or_admin_required
 def rapports_poste(request):
 
     lookup_params = dict(request.GET.items())
@@ -468,4 +474,34 @@ def employe_apercu(request, employe_id):
     }
     return render_to_response('admin/rh/employe/apercu.html', c, RequestContext(request))
 
+def organigrammes_employe(request, id):
+
+    poste = get_object_or_404(rh.Poste, pk=id)
+
+    graph = nx.DiGraph()
+    dossiers = rh.Dossier.objects.select_related('employe', 'poste', 'poste__implantation').filter((Q(poste__date_fin__gt=date.today()) | Q(poste__date_fin=None)) & (Q(poste__date_debut__lt=date.today()) | Q(poste__date_debut=None)) ).exclude(poste__supprime=True).exclude(poste__responsable=None).all()
+    dossiers_by_poste = dict((d.poste_id, d) for d in rh.Dossier.objects.select_related('employe', 'poste').all())
+    for p in rh.Poste.objects.all():
+        graph.add_node(p.id)
+
+        for d in dossiers:
+            if d.poste.responsable_id != d.poste.id:
+                graph.add_edge(dossiers_by_poste[d.poste.responsable_id].poste_id, d.poste.id)
 
+    graph = nx.bfs_tree(graph, id)
+    a = nx.to_agraph(graph)
+    for n in a.nodes():
+        d = dossiers_by_poste[int(n)]
+        n.attr['label'] = u"%s\\n%s\\n%s" % (d.poste.nom, "%s %s" % (d.employe.nom.upper(), d.employe.prenom), d.poste.implantation)
+
+    a.graph_attr['normalize'] = True
+    a.graph_attr['level'] = 2
+    a.layout(prog='dot')
+
+    png = a.draw(format='png')
+
+
+    c = {
+        'png': b64encode(png)
+    }
+    return render_to_response('rh/organigrammes/employe.html', c, RequestContext(request))