forked from ScoDoc/ScoDoc
Fixed graph generation with pydot and added unit test
This commit is contained in:
parent
30f88dfd4f
commit
d93b5688ae
@ -98,7 +98,7 @@ def defMenuStats(context, formsemestre_id):
|
||||
"title": "Graphe des parcours",
|
||||
"endpoint": "notes.formsemestre_graph_parcours",
|
||||
"args": {"formsemestre_id": formsemestre_id},
|
||||
"enabled": scu.WITH_PYDOT,
|
||||
"enabled": True,
|
||||
},
|
||||
{
|
||||
"title": "Codes des parcours",
|
||||
|
@ -38,6 +38,7 @@ import datetime
|
||||
from operator import itemgetter
|
||||
|
||||
from flask import url_for, g
|
||||
import pydot
|
||||
|
||||
import app.scodoc.sco_utils as scu
|
||||
from app.scodoc import notesdb as ndb
|
||||
@ -1255,8 +1256,6 @@ def graph_parcours(
|
||||
statut="",
|
||||
):
|
||||
""""""
|
||||
if not scu.WITH_PYDOT:
|
||||
raise ScoValueError("pydot module is not installed")
|
||||
etuds, bacs, bacspecialites, annee_bacs, civilites, statuts = tsp_etud_list(
|
||||
context,
|
||||
formsemestre_id,
|
||||
@ -1342,10 +1341,10 @@ def graph_parcours(
|
||||
edges[(s["formsemestre_id"], nid)].add(etudid)
|
||||
diploma_nodes.append(nid)
|
||||
#
|
||||
g = scu.pydot.graph_from_edges(list(edges.keys()))
|
||||
g = scu.graph_from_edges(list(edges.keys()))
|
||||
for fid in isolated_nodes:
|
||||
if not fid in connected_nodes:
|
||||
n = scu.pydot.Node(name=fid)
|
||||
n = pydot.Node(name=fid)
|
||||
g.add_node(n)
|
||||
g.set("rankdir", "LR") # left to right
|
||||
g.set_fontname("Helvetica")
|
||||
@ -1353,7 +1352,7 @@ def graph_parcours(
|
||||
g.set_bgcolor("#fffff0") # ou 'transparent'
|
||||
# titres des semestres:
|
||||
for s in sems.values():
|
||||
n = scu.pydot_get_node(g, s["formsemestre_id"])
|
||||
n = g.get_node(s["formsemestre_id"])[0]
|
||||
log("s['formsemestre_id'] = %s" % s["formsemestre_id"])
|
||||
log("n=%s" % n)
|
||||
log("get=%s" % g.get_node(s["formsemestre_id"]))
|
||||
@ -1378,31 +1377,31 @@ def graph_parcours(
|
||||
n.set_shape("box")
|
||||
n.set_URL("formsemestre_status?formsemestre_id=" + s["formsemestre_id"])
|
||||
# semestre de depart en vert
|
||||
n = scu.pydot_get_node(g, formsemestre_id)
|
||||
n = g.get_node(formsemestre_id)[0]
|
||||
n.set_color("green")
|
||||
# demissions en rouge, octagonal
|
||||
for nid in dem_nodes.values():
|
||||
n = scu.pydot_get_node(g, nid)
|
||||
n = g.get_node(nid)[0]
|
||||
n.set_color("red")
|
||||
n.set_shape("octagon")
|
||||
n.set("label", "Dem.")
|
||||
|
||||
# NAR en rouge, Mcircle
|
||||
for nid in nar_nodes.values():
|
||||
n = scu.pydot_get_node(g, nid)
|
||||
n = g.get_node(nid)[0]
|
||||
n.set_color("red")
|
||||
n.set_shape("Mcircle")
|
||||
n.set("label", sco_codes_parcours.NAR)
|
||||
# diplomes:
|
||||
for nid in diploma_nodes:
|
||||
n = scu.pydot_get_node(g, nid)
|
||||
n = g.get_node(nid)[0]
|
||||
n.set_color("red")
|
||||
n.set_shape("ellipse")
|
||||
n.set("label", "Diplome") # bug si accent (pas compris pourquoi)
|
||||
# Arètes:
|
||||
bubbles = {} # substitue titres pour bulle aides: src_id:dst_id : etud_descr
|
||||
for (src_id, dst_id) in edges.keys():
|
||||
e = g.get_edge(src_id, dst_id)
|
||||
e = g.get_edge(src_id, dst_id)[0]
|
||||
e.set("arrowhead", "normal")
|
||||
e.set("arrowsize", 1)
|
||||
e.set_label(len(edges[(src_id, dst_id)]))
|
||||
@ -1416,7 +1415,7 @@ def graph_parcours(
|
||||
# Genere graphe
|
||||
_, path = tempfile.mkstemp(".gr")
|
||||
g.write(path=path, format=format)
|
||||
data = open(path, "r").read()
|
||||
data = open(path, "rb").read()
|
||||
log("dot generated %d bytes in %s format" % (len(data), format))
|
||||
if not data:
|
||||
log("graph.to_string=%s" % g.to_string())
|
||||
@ -1528,14 +1527,16 @@ def formsemestre_graph_parcours(
|
||||
REQUEST.RESPONSE.setHeader("content-type", "image/png")
|
||||
return doc
|
||||
elif format == "html":
|
||||
url_kw = {
|
||||
"scodoc_dept": g.scodoc_dept,
|
||||
"formsemestre_id": formsemestre_id,
|
||||
"bac": bac,
|
||||
"specialite": bacspecialite,
|
||||
"civilite": civilite,
|
||||
"statut": statut,
|
||||
}
|
||||
if only_primo:
|
||||
op = "only_primo=on&"
|
||||
else:
|
||||
op = ""
|
||||
url = six.moves.urllib.parse.quote(
|
||||
"formsemestre_graph_parcours?formsemestre_id=%s&%sbac=%s&bacspecialite=%s&civilite=%s&statut=%s&format="
|
||||
% (formsemestre_id, op, bac, bacspecialite, civilite, statut)
|
||||
)
|
||||
url_kw["only_primo"] = "on"
|
||||
(
|
||||
doc,
|
||||
etuds,
|
||||
@ -1583,12 +1584,11 @@ def formsemestre_graph_parcours(
|
||||
),
|
||||
"""<p>Origine et devenir des étudiants inscrits dans %(titreannee)s"""
|
||||
% sem,
|
||||
# En Debian 4, dot ne genere pas du pdf, et epstopdf ne marche pas sur le .ps ou ps2 générés par dot
|
||||
# mais c'est OK en Debian 5
|
||||
"""(<a href="%spdf">version pdf</a>""" % url,
|
||||
""", <a href="%spng">image PNG</a>)""" % url,
|
||||
"""(<a href="%s">version pdf</a>"""
|
||||
% url_for("notes.formsemestre_graph_parcours", format="pdf", **url_kw),
|
||||
""", <a href="%s">image PNG</a>)"""
|
||||
% url_for("notes.formsemestre_graph_parcours", format="png", **url_kw),
|
||||
"""</p>""",
|
||||
"""<p class="help">Cette page ne s'affiche correctement que sur les navigateurs récents.</p>""",
|
||||
"""<p class="help">Le graphe permet de suivre les étudiants inscrits dans le semestre
|
||||
sélectionné (dessiné en vert). Chaque rectangle représente un semestre (cliquez dedans
|
||||
pour afficher son tableau de bord). Les flèches indiquent le nombre d'étudiants passant
|
||||
|
@ -36,6 +36,7 @@ import json
|
||||
from hashlib import md5
|
||||
import numbers
|
||||
import os
|
||||
import pydot
|
||||
import re
|
||||
import six
|
||||
import six.moves._thread
|
||||
@ -673,39 +674,27 @@ def sem_decale_str(sem):
|
||||
return ""
|
||||
|
||||
|
||||
# Graphes (optionnel pour ne pas accroitre les dependances de ScoDoc)
|
||||
try:
|
||||
import pydot
|
||||
|
||||
WITH_PYDOT = True
|
||||
except:
|
||||
WITH_PYDOT = False
|
||||
|
||||
if WITH_PYDOT:
|
||||
# check API (incompatible change after pydot version 0.9.10: scodoc install may use old or new version)
|
||||
junk_graph = pydot.Dot("junk")
|
||||
junk_graph.add_node(pydot.Node("a"))
|
||||
n = junk_graph.get_node("a")
|
||||
if type(n) == type([]): # "modern" pydot
|
||||
|
||||
def pydot_get_node(g, name):
|
||||
r = g.get_node(name)
|
||||
if not r:
|
||||
return r
|
||||
else:
|
||||
return r[0]
|
||||
|
||||
else: # very old pydot
|
||||
|
||||
def pydot_get_node(g, name):
|
||||
return g.get_node(name)
|
||||
|
||||
|
||||
def is_valid_mail(email):
|
||||
"""True if well-formed email address"""
|
||||
return re.match(r"^.+@.+\..{2,3}$", email)
|
||||
|
||||
|
||||
def graph_from_edges(edges, graph_name="mygraph"):
|
||||
"""Crée un graph pydot
|
||||
à partir d'une liste d'arêtes [ (n1, n2), (n2, n3), ... ]
|
||||
où n1, n2, ... sont des chaînes donnant l'id des nœuds.
|
||||
|
||||
Fonction remplaçant celle de pydot qui est buggée.
|
||||
"""
|
||||
nodes = set([it for tup in edges for it in tup])
|
||||
graph = pydot.Dot(graph_name)
|
||||
for n in nodes:
|
||||
graph.add_node(pydot.Node(n))
|
||||
for e in edges:
|
||||
graph.add_edge(pydot.Edge(src=e[0], dst=e[1]))
|
||||
return graph
|
||||
|
||||
|
||||
ICONSIZES = {} # name : (width, height) cache image sizes
|
||||
|
||||
|
||||
|
@ -1,37 +0,0 @@
|
||||
# essai pydot (bug ?)
|
||||
# EV, sept 2011
|
||||
|
||||
import pydot
|
||||
|
||||
print 'pydot version:', pydot.__version__
|
||||
|
||||
g = pydot.Dot('graphname')
|
||||
g.add_node(pydot.Node('a'))
|
||||
g.add_node(pydot.Node('b'))
|
||||
|
||||
|
||||
n = g.get_node('a')
|
||||
|
||||
print n
|
||||
print 'nodes names = %s' % [ x.get_name() for x in g.get_node_list() ]
|
||||
|
||||
edges = [ ('a','b'), ('b','c'), ('c','d') ]
|
||||
g = pydot.graph_from_edges(edges)
|
||||
print 'nodes names = %s' % [ x.get_name() for x in g.get_node_list() ]
|
||||
|
||||
if not len(g.get_node_list()):
|
||||
print 'bug: empty node list !' # incompatibility versions python / pydot
|
||||
|
||||
# Les fleches ?
|
||||
for (src_id, dst_id) in edges:
|
||||
e = g.get_edge(src_id, dst_id)
|
||||
e.set('arrowhead', 'normal')
|
||||
e.set( 'arrowsize', 2 )
|
||||
e.set_label( str( (src_id, dst_id) ) )
|
||||
e.set_fontname('Helvetica')
|
||||
e.set_fontsize(8.0)
|
||||
|
||||
g.write_jpeg('/tmp/graph_from_edges_dot.jpg', prog='dot') # ok sur ScoDoc / Debian 5, pas de fleches en Debian 6
|
||||
# cf https://www-lipn.univ-paris13.fr/projects/scodoc/ticket/190
|
||||
|
||||
|
@ -39,6 +39,7 @@ pluggy==0.13.1
|
||||
psycopg2==2.9.1
|
||||
py==1.10.0
|
||||
pycparser==2.20
|
||||
pydot==1.4.2
|
||||
pylibmc==1.6.1
|
||||
pyOpenSSL==20.0.1
|
||||
pyparsing==2.4.7
|
||||
|
Loading…
Reference in New Issue
Block a user