Add mixed dataviz. CLI not operational yet.
This commit is contained in:
parent
b33370daaf
commit
3b7a3df51c
148
get.py
148
get.py
@ -97,7 +97,6 @@ def cli_check():
|
|||||||
global depts
|
global depts
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(description="Process some departments.")
|
parser = argparse.ArgumentParser(description="Process some departments.")
|
||||||
parser.add_argument("--techno", action="store_true", help="Enable TECHNO mode")
|
|
||||||
parser.add_argument("depts", nargs="*", help="List of departments")
|
parser.add_argument("depts", nargs="*", help="List of departments")
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--base",
|
"--base",
|
||||||
@ -146,7 +145,6 @@ def cli_check():
|
|||||||
args.depts.insert(0, args.optimize)
|
args.depts.insert(0, args.optimize)
|
||||||
|
|
||||||
Options.base_year = args.base
|
Options.base_year = args.base
|
||||||
Options.techno = args.techno
|
|
||||||
depts = args.depts
|
depts = args.depts
|
||||||
orderkey = "_".join(depts)
|
orderkey = "_".join(depts)
|
||||||
|
|
||||||
@ -181,32 +179,37 @@ def write_conf(key, obj):
|
|||||||
|
|
||||||
conf = read_conf(orderkey)
|
conf = read_conf(orderkey)
|
||||||
|
|
||||||
|
defaults = {
|
||||||
|
"spacing": 14,
|
||||||
|
"thickness": 6,
|
||||||
|
"fontsize_name": 10,
|
||||||
|
"fontsize_count": 14,
|
||||||
|
"width": 1300,
|
||||||
|
"height": 0,
|
||||||
|
"hmargin": 20,
|
||||||
|
"parcours_separator": "/",
|
||||||
|
"year_separator": " ",
|
||||||
|
"rank_separator": "",
|
||||||
|
"diplome_separator": "",
|
||||||
|
"reuse": "yes",
|
||||||
|
"optimize": "yes",
|
||||||
|
"main_filter": 0,
|
||||||
|
"secondary_filter": 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def conf_value(xkey: str):
|
def conf_value(xkey: str):
|
||||||
"""Manage default values"""
|
"""Manage default values"""
|
||||||
defaults = {
|
|
||||||
"spacing": 14,
|
|
||||||
"thickness": 6,
|
|
||||||
"fontsize_name": 10,
|
|
||||||
"fontsize_count": 14,
|
|
||||||
"width": 1300,
|
|
||||||
"height": 0,
|
|
||||||
"hmargin": 20,
|
|
||||||
"parcours_separator": "/",
|
|
||||||
"year_separator": " ",
|
|
||||||
"rank_separator": "",
|
|
||||||
"diplome_separator": "",
|
|
||||||
"reuse": "yes",
|
|
||||||
"optimize": "yes",
|
|
||||||
}
|
|
||||||
if xkey in conf:
|
if xkey in conf:
|
||||||
return conf[xkey]
|
return conf[xkey]
|
||||||
if xkey in defaults:
|
if xkey in defaults:
|
||||||
return defaults[xkey]
|
return defaults[xkey]
|
||||||
if xkey[-9:] == "separator":
|
if xkey[-9:] == "separator":
|
||||||
return " "
|
return " "
|
||||||
if xkey == "nick" or xkey == "displayname":
|
if xkey == "nick":
|
||||||
return "{diplome}{rank}{multidepartment}{modalite}{parcours}{year}"
|
return "{diplome}{rank}{multidepartment}{modalite}{parcours}{year}"
|
||||||
|
if xkey == "displayname":
|
||||||
|
return "{diplome}{rank}{multidepartment}{modaliteshort}{parcours}"
|
||||||
if xkey == "extnick":
|
if xkey == "extnick":
|
||||||
return "{ext}{rank}{multidepartment}{diplomenobut}{modaliteshort}"
|
return "{ext}{rank}{multidepartment}{diplomenobut}{modaliteshort}"
|
||||||
if xkey == "orders":
|
if xkey == "orders":
|
||||||
@ -588,12 +591,6 @@ def analyse_depts():
|
|||||||
else:
|
else:
|
||||||
studentsummary["civilite"] = "?"
|
studentsummary["civilite"] = "?"
|
||||||
bacs.add(studentsummary["bac"])
|
bacs.add(studentsummary["bac"])
|
||||||
# We skip non-techno students if we are in techno mode
|
|
||||||
# If we want a mixed reporting, maybe we should change this
|
|
||||||
if (
|
|
||||||
Options.techno and studentsummary["bac"][:2] != "ST"
|
|
||||||
): # TODO: change this
|
|
||||||
continue
|
|
||||||
if bucket in studentsummary["cursus"]:
|
if bucket in studentsummary["cursus"]:
|
||||||
semestreerreur = int(bucket) + 1
|
semestreerreur = int(bucket) + 1
|
||||||
warning(
|
warning(
|
||||||
@ -1007,7 +1004,9 @@ for etudid in student.keys():
|
|||||||
goodred[ddd] += 1
|
goodred[ddd] += 1
|
||||||
output = finaloutput + " " + etud["nickshort"][lastyear]
|
output = finaloutput + " " + etud["nickshort"][lastyear]
|
||||||
etud["nickshort"][lastyear + 1] = output
|
etud["nickshort"][lastyear + 1] = output
|
||||||
displaynames[output] = finals[finaloutput] + etud["nickshort"][lastyear]
|
displaynames[output] = (
|
||||||
|
finals[finaloutput] + displaynames[etud["nickshort"][lastyear]]
|
||||||
|
)
|
||||||
(firstsem, firstyear) = (
|
(firstsem, firstyear) = (
|
||||||
(etud["short"][1], 1)
|
(etud["short"][1], 1)
|
||||||
if etud["short"][1] != None
|
if etud["short"][1] != None
|
||||||
@ -1054,8 +1053,15 @@ for etudid in student.keys():
|
|||||||
|
|
||||||
|
|
||||||
class Filter:
|
class Filter:
|
||||||
|
# Filter on students to be considered
|
||||||
|
# 1 consider only technological baccalaureates, statistics are always asked
|
||||||
|
# 2 consider only women, because gender statistics are frequently asked
|
||||||
|
# 4 consider only incoming students (primo-entrants) in first year of the cohort
|
||||||
|
# 8 consider only people having a first year, not parallel entries
|
||||||
TECHNO = 1
|
TECHNO = 1
|
||||||
WOMAN = 2
|
WOMAN = 2
|
||||||
|
PRIMO = 4
|
||||||
|
MAIN = 8
|
||||||
|
|
||||||
|
|
||||||
def bags_from_students(student, filter=0):
|
def bags_from_students(student, filter=0):
|
||||||
@ -1253,32 +1259,31 @@ def get_layer_structure(newls, node_structure, spacing, hmargin, height):
|
|||||||
for j in ls["olayer"]:
|
for j in ls["olayer"]:
|
||||||
lhi = 0
|
lhi = 0
|
||||||
lho = 0
|
lho = 0
|
||||||
|
slhi = 0
|
||||||
|
slho = 0
|
||||||
k = node_structure[j]
|
k = node_structure[j]
|
||||||
for prev_node in k["prev"]:
|
for prev_node in k["prev"]:
|
||||||
lhi += prev_node[1]
|
lhi += prev_node[1]
|
||||||
|
slhi += prev_node[2]
|
||||||
for next_node in k["next"]:
|
for next_node in k["next"]:
|
||||||
lho += next_node[1]
|
lho += next_node[1]
|
||||||
|
slho += next_node[2]
|
||||||
k["size"] = max(lhi, lho)
|
k["size"] = max(lhi, lho)
|
||||||
|
k["ssize"] = max(slhi, slho)
|
||||||
k["in"] = lhi
|
k["in"] = lhi
|
||||||
k["out"] = lho
|
k["out"] = lho
|
||||||
if lhi != lho and lhi * lho != 0:
|
if lhi != lho and lhi * lho != 0:
|
||||||
print(f"BUG1: {j} {k} {lhi} {lho}")
|
warning(f"BUG1: {j} {k} {lhi} {lho}")
|
||||||
print(json.dumps(layer_structure, indent=2))
|
|
||||||
print(json.dumps(node_structure, indent=2))
|
|
||||||
ls["inout"] += k["size"]
|
ls["inout"] += k["size"]
|
||||||
layer_structure.append(ls)
|
layer_structure.append(ls)
|
||||||
if height == 0:
|
if height == 0:
|
||||||
debug("coucou")
|
|
||||||
minheight = 0
|
minheight = 0
|
||||||
for i in range(5):
|
for i in range(5):
|
||||||
ls = layer_structure[i]
|
ls = layer_structure[i]
|
||||||
new_minheight = spacing * (ls["num"] - 1) + 2 * hmargin + 2 * ls["inout"]
|
new_minheight = spacing * (ls["num"] - 1) + 2 * hmargin + 2 * ls["inout"]
|
||||||
debug(f"{new_minheight} / {minheight}")
|
|
||||||
if new_minheight > minheight:
|
if new_minheight > minheight:
|
||||||
minheight = new_minheight
|
minheight = new_minheight
|
||||||
height = (1 + (minheight // 150)) * 150
|
height = (1 + (minheight // 150)) * 150
|
||||||
debug(f"{height}")
|
|
||||||
|
|
||||||
for i in range(5):
|
for i in range(5):
|
||||||
ls = layer_structure[i]
|
ls = layer_structure[i]
|
||||||
ls["density"] = ls["inout"] / (
|
ls["density"] = ls["inout"] / (
|
||||||
@ -1300,6 +1305,8 @@ def get_layer_structure(newls, node_structure, spacing, hmargin, height):
|
|||||||
ns = node_structure[j]
|
ns = node_structure[j]
|
||||||
ns["top"] = supp_spacing + spacing + cs
|
ns["top"] = supp_spacing + spacing + cs
|
||||||
h = ns["size"] / realdensity
|
h = ns["size"] / realdensity
|
||||||
|
sh = ns["ssize"] / realdensity
|
||||||
|
ns["middle"] = ns["top"] + sh
|
||||||
cs = ns["bottom"] = ns["top"] + h
|
cs = ns["bottom"] = ns["top"] + h
|
||||||
return realdensity, height, layer_structure
|
return realdensity, height, layer_structure
|
||||||
|
|
||||||
@ -1308,6 +1315,14 @@ def nodestructure_from_bags(bags, sbags=None):
|
|||||||
node_structure = {}
|
node_structure = {}
|
||||||
layers = [[], [], [], [], []]
|
layers = [[], [], [], [], []]
|
||||||
edges = []
|
edges = []
|
||||||
|
union_sbag = {}
|
||||||
|
if sbags is not None:
|
||||||
|
for layer, layernodes in enumerate(sbags):
|
||||||
|
for startnode in layernodes:
|
||||||
|
for endnode in layernodes[startnode]:
|
||||||
|
if startnode not in union_sbag:
|
||||||
|
union_sbag[startnode] = {}
|
||||||
|
union_sbag[startnode][endnode] = layernodes[startnode][endnode]
|
||||||
for layer, layernodes in enumerate(bags):
|
for layer, layernodes in enumerate(bags):
|
||||||
for startnode in layernodes:
|
for startnode in layernodes:
|
||||||
# if startnode[-1] == "*":
|
# if startnode[-1] == "*":
|
||||||
@ -1316,24 +1331,30 @@ def nodestructure_from_bags(bags, sbags=None):
|
|||||||
# if endnode[-1] == "*":
|
# if endnode[-1] == "*":
|
||||||
# continue
|
# continue
|
||||||
weight = layernodes[startnode][endnode]
|
weight = layernodes[startnode][endnode]
|
||||||
|
if startnode in union_sbag and endnode in union_sbag[startnode]:
|
||||||
|
sweight = union_sbag[startnode][endnode]
|
||||||
|
elif sbags is None:
|
||||||
|
sweight = -1
|
||||||
|
else:
|
||||||
|
sweight = 0
|
||||||
if endnode not in node_structure:
|
if endnode not in node_structure:
|
||||||
node_structure[endnode] = {
|
node_structure[endnode] = {
|
||||||
"prev": [[startnode, weight]],
|
"prev": [[startnode, weight, sweight]],
|
||||||
"next": [],
|
"next": [],
|
||||||
"layer": layer + 1,
|
"layer": layer + 1,
|
||||||
}
|
}
|
||||||
layers[layer + 1].append(endnode)
|
layers[layer + 1].append(endnode)
|
||||||
else:
|
else:
|
||||||
node_structure[endnode]["prev"].append([startnode, weight])
|
node_structure[endnode]["prev"].append([startnode, weight, sweight])
|
||||||
if startnode not in node_structure:
|
if startnode not in node_structure:
|
||||||
node_structure[startnode] = {
|
node_structure[startnode] = {
|
||||||
"prev": [],
|
"prev": [],
|
||||||
"next": [[endnode, weight]],
|
"next": [[endnode, weight, sweight]],
|
||||||
"layer": layer,
|
"layer": layer,
|
||||||
}
|
}
|
||||||
layers[layer].append(startnode)
|
layers[layer].append(startnode)
|
||||||
else:
|
else:
|
||||||
node_structure[startnode]["next"].append([endnode, weight])
|
node_structure[startnode]["next"].append([endnode, weight, sweight])
|
||||||
edges.append([startnode, endnode, weight])
|
edges.append([startnode, endnode, weight])
|
||||||
return node_structure, layers, edges
|
return node_structure, layers, edges
|
||||||
|
|
||||||
@ -1366,6 +1387,17 @@ def compute_svg(height, padding, realdensity, node_structure):
|
|||||||
2 * thickness,
|
2 * thickness,
|
||||||
ns["bottom"] - ns["top"],
|
ns["bottom"] - ns["top"],
|
||||||
fill=col,
|
fill=col,
|
||||||
|
opacity=0.5,
|
||||||
|
stroke_width=0.1,
|
||||||
|
stroke="#808080",
|
||||||
|
)
|
||||||
|
g1.append(r)
|
||||||
|
r = drawsvg.Rectangle(
|
||||||
|
xpos - thickness,
|
||||||
|
ns["top"],
|
||||||
|
2 * thickness,
|
||||||
|
ns["middle"] - ns["top"],
|
||||||
|
fill=col,
|
||||||
stroke_width=0.2,
|
stroke_width=0.2,
|
||||||
stroke="black",
|
stroke="black",
|
||||||
)
|
)
|
||||||
@ -1426,16 +1458,20 @@ def compute_svg(height, padding, realdensity, node_structure):
|
|||||||
ns["next"].sort(key=lambda x: node_structure[x[0]]["top"])
|
ns["next"].sort(key=lambda x: node_structure[x[0]]["top"])
|
||||||
start = ns["top"]
|
start = ns["top"]
|
||||||
for link in ns["prev"]:
|
for link in ns["prev"]:
|
||||||
ysize = link[-1]
|
ysize = link[1]
|
||||||
|
sysize = link[2]
|
||||||
link.append(start)
|
link.append(start)
|
||||||
|
link.append(start + sysize / realdensity)
|
||||||
start += ysize / realdensity
|
start += ysize / realdensity
|
||||||
link.append(start)
|
link.append(start)
|
||||||
for n in node_structure:
|
for n in node_structure:
|
||||||
ns = node_structure[n]
|
ns = node_structure[n]
|
||||||
start = ns["top"]
|
start = ns["top"]
|
||||||
for link in ns["next"]:
|
for link in ns["next"]:
|
||||||
ysize = link[-1]
|
ysize = link[1]
|
||||||
|
sysize = link[2]
|
||||||
link.append(start)
|
link.append(start)
|
||||||
|
link.append(start + sysize / realdensity)
|
||||||
start += ysize / realdensity
|
start += ysize / realdensity
|
||||||
link.append(start)
|
link.append(start)
|
||||||
targets = node_structure[link[0]]
|
targets = node_structure[link[0]]
|
||||||
@ -1446,7 +1482,7 @@ def compute_svg(height, padding, realdensity, node_structure):
|
|||||||
if target == None:
|
if target == None:
|
||||||
print(f"BUG: {n},{ns},{t}")
|
print(f"BUG: {n},{ns},{t}")
|
||||||
sys.exit(5)
|
sys.exit(5)
|
||||||
|
# At this point, link has values target_name, size, secondary_size, top, middle, bottom
|
||||||
posxa = columns[ns["layer"]] + thickness
|
posxa = columns[ns["layer"]] + thickness
|
||||||
posxb = columns[targets["layer"]] - thickness
|
posxb = columns[targets["layer"]] - thickness
|
||||||
posxc = (3 * posxa + posxb) / 4
|
posxc = (3 * posxa + posxb) / 4
|
||||||
@ -1454,12 +1490,31 @@ def compute_svg(height, padding, realdensity, node_structure):
|
|||||||
grad = drawsvg.LinearGradient(posxa, 0, posxb, 0)
|
grad = drawsvg.LinearGradient(posxa, 0, posxb, 0)
|
||||||
grad.add_stop(0, ns["color"], opacity=0.5)
|
grad.add_stop(0, ns["color"], opacity=0.5)
|
||||||
grad.add_stop(1, targets["color"], opacity=0.5)
|
grad.add_stop(1, targets["color"], opacity=0.5)
|
||||||
p = drawsvg.Path(fill=grad, stroke_width=0)
|
posyat = link[3]
|
||||||
p.M(posxa, link[-2])
|
posyam = link[4]
|
||||||
p.C(posxc, link[-2], posxd, target[-2], posxb, target[-2])
|
posyab = link[5]
|
||||||
p.L(posxb, target[-1])
|
posybt = target[3]
|
||||||
p.C(posxd, target[-1], posxc, link[-1], posxa, link[-1])
|
posybm = target[4]
|
||||||
|
posybb = target[5]
|
||||||
|
p = drawsvg.Path(fill=grad, stroke="#000", stroke_width=0, opacity=0.8)
|
||||||
|
p.M(posxa, posyab)
|
||||||
|
p.C(posxc, posyab, posxd, posybb, posxb, posybb)
|
||||||
|
p.L(posxb, posybt)
|
||||||
|
p.C(posxd, posybt, posxc, posyat, posxa, posyat)
|
||||||
p.Z()
|
p.Z()
|
||||||
|
p.append_title(
|
||||||
|
f"{displaynames[n]}=>{displaynames[link[0]]}: {link[2]}/{link[1]}"
|
||||||
|
)
|
||||||
|
g2.append(p)
|
||||||
|
p = drawsvg.Path(fill=grad, stroke="#000", stroke_width=0)
|
||||||
|
p.M(posxa, posyam)
|
||||||
|
p.C(posxc, posyam, posxd, posybm, posxb, posybm)
|
||||||
|
p.L(posxb, posybt)
|
||||||
|
p.C(posxd, posybt, posxc, posyat, posxa, posyat)
|
||||||
|
p.Z()
|
||||||
|
p.append_title(
|
||||||
|
f"{displaynames[n]}=>{displaynames[link[0]]}: {link[2]}/{link[1]}"
|
||||||
|
)
|
||||||
g2.append(p)
|
g2.append(p)
|
||||||
d.append(g2)
|
d.append(g2)
|
||||||
d.append(g1)
|
d.append(g1)
|
||||||
@ -1473,8 +1528,9 @@ def printsvg():
|
|||||||
spacing = conf_value("spacing")
|
spacing = conf_value("spacing")
|
||||||
height = conf_value("height")
|
height = conf_value("height")
|
||||||
hmargin = conf_value("hmargin")
|
hmargin = conf_value("hmargin")
|
||||||
bags = bags_from_students(student, 0)
|
bags = bags_from_students(student, conf_value("main_filter"))
|
||||||
node_structure, layers, edges = nodestructure_from_bags(bags)
|
sbags = bags_from_students(student, conf_value("secondary_filter"))
|
||||||
|
node_structure, layers, edges = nodestructure_from_bags(bags, sbags)
|
||||||
filename = "best-" + orderkey
|
filename = "best-" + orderkey
|
||||||
if Options.restart:
|
if Options.restart:
|
||||||
try:
|
try:
|
||||||
|
Loading…
Reference in New Issue
Block a user