Refactorize debug and warning functions

add switches to control optimization
This commit is contained in:
Jean-Christophe Dubacq 2024-10-24 20:11:39 +02:00
parent 67bab0af54
commit 474d2dffbd

219
get.py
View File

@ -6,6 +6,7 @@ import csv
import json import json
import os import os
import pdb # used for debugging import pdb # used for debugging
import inspect # used for debugging
import random import random
import sys import sys
@ -26,6 +27,33 @@ def die(msg: str, status=3):
sys.exit(status) sys.exit(status)
def debug(*args):
if not hasattr(debug, "counter"):
debug.counter = 1 # Initialize the counter if it doesn't exist
else:
debug.counter += 1
caller_frame = inspect.currentframe().f_back
where = str(caller_frame.f_lineno) + "@" + caller_frame.f_code.co_name
if len(args) > 0:
print(f"[DEBUG {debug.counter}:{where}] " + str(args[0]), args[1:])
else:
print(f"[DEBUG {debug.counter}:{where}] (no reason given)")
def warning(*args):
if len(args) > 0:
print("[WARNING] " + str(args[0]), args[1:])
else:
print("[WARNING] (no reason given)")
def info(*args):
if len(args) > 0:
print("[INFO] " + str(args[0]), args[1:])
else:
print("[INFO] (no reason given)")
load_dotenv(".env") load_dotenv(".env")
SCODOC_SERVER = os.environ.get("SCODOC_SERVER") or "http://localhost:5000" SCODOC_SERVER = os.environ.get("SCODOC_SERVER") or "http://localhost:5000"
@ -39,7 +67,7 @@ SCODOC_PASSWORD = os.environ.get("SCODOC_PASSWORD") or die(
API_URL = f"{SCODOC_SERVER}/ScoDoc/api" API_URL = f"{SCODOC_SERVER}/ScoDoc/api"
# TODO : refactor globals # TODO : refactor globals
debug = True # Not used DEBUG = True # Not used
BLOCKING = True # Die if csv is incorrect BLOCKING = True # Die if csv is incorrect
# TODO : refactor / put globals in a class, eg Config # TODO : refactor / put globals in a class, eg Config
@ -47,7 +75,11 @@ depts = []
orderkey = "" orderkey = ""
def blockordie(status=2): def blockordie(reason: str = "", status: int = 2):
if reason:
print(reason)
else:
print("Blocking, no reason given")
if BLOCKING: if BLOCKING:
sys.exit(status) sys.exit(status)
@ -66,17 +98,52 @@ def cli_check():
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("--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",
"-b", "-b",
type=int, type=int,
choices=range(2000, 2667), choices=range(2000, 2067),
default=2021, default=2021,
help="base year for the cohort (integer between 2000 and 2666)", help="base year for the cohort (integer between 2000 and 2666)",
) )
optimize_group = parser.add_mutually_exclusive_group()
optimize_group.add_argument(
"--reuse", action="store_true", help="Reuse mode, sets value to 0"
)
optimize_group.add_argument(
"--optimize",
type=str,
nargs="?",
const="100", # Default value if --optimize is used without specifying n
help="Optimize mode, takes an optional integer (default is 100, or 300 if no optimization option specified)",
)
optimize_group.add_argument(
"--restart",
type=str,
nargs="?",
const="300", # Default value if --restart is used without specifying n
help="Restart & Optimize mode, takes an optional integer (default is 300)",
)
args = parser.parse_args() args = parser.parse_args()
Options.restart = False
if args.reuse:
Options.optimize = 0
elif args.restart is not None:
Options.restart = True
try:
Options.optimize = -int(args.restart)
except (TypeError, ValueError):
Options.optimize = -300
if args.restart:
args.depts.insert(0, args.restart)
else:
try:
Options.optimize = int(args.optimize)
except (TypeError, ValueError):
Options.optimize = 300
if args.optimize:
args.depts.insert(0, args.optimize)
Options.base_year = args.base Options.base_year = args.base
Options.techno = args.techno Options.techno = args.techno
@ -129,6 +196,8 @@ def conf_value(xkey: str):
"year_separator": " ", "year_separator": " ",
"rank_separator": "", "rank_separator": "",
"diplome_separator": "", "diplome_separator": "",
"reuse": "yes",
"optimize": "yes",
} }
if xkey in conf: if xkey in conf:
return conf[xkey] return conf[xkey]
@ -176,8 +245,7 @@ def read_theme():
if len(row) == 0: if len(row) == 0:
continue continue
elif len(row[0]) == 0: elif len(row[0]) == 0:
print("Wrong line in theme : " + str(row)) blockordie("Wrong line in theme : " + str(row))
blockordie()
elif row[0][0] == "#": elif row[0][0] == "#":
continue continue
else: else:
@ -210,8 +278,7 @@ def read_redirects():
if len(row) == 0: if len(row) == 0:
continue continue
elif len(row[0]) == 0: elif len(row[0]) == 0:
print("Wrong line in redirect : " + str(row)) blockordie("Wrong line in redirect : " + str(row))
blockordie()
elif row[0][0] == "#": elif row[0][0] == "#":
continue continue
else: else:
@ -227,7 +294,7 @@ token = None
def get_json(url: str, params=None): def get_json(url: str, params=None):
print(f"Requesting {url}") debug(f"Requesting {url}")
global token global token
if token == None: if token == None:
url_token = f"{API_URL}/tokens" url_token = f"{API_URL}/tokens"
@ -237,18 +304,19 @@ def get_json(url: str, params=None):
if response.status_code == 200: if response.status_code == 200:
token = response.json().get("token") token = response.json().get("token")
else: else:
print( blockordie(
f"Erreur de récupération de token: {response.status_code} - {response.text}" f"Erreur de récupération de token: {response.status_code} - {response.text}",
status=1,
) )
sys.exit(1)
headers = {"Authorization": f"Bearer {token}"} headers = {"Authorization": f"Bearer {token}"}
response = requests.get(url, headers=headers, params=params) response = requests.get(url, headers=headers, params=params)
if response.status_code == 200: if response.status_code == 200:
# Afficher la réponse JSON # Afficher la réponse JSON
return response.json() return response.json()
else: else:
print(f"Erreur avec {url}: {response.status_code} - {response.text}") blockordie(
sys.exit(1) f"Erreur avec {url}: {response.status_code} - {response.text}", status=1
)
formsem_dept = {} formsem_dept = {}
@ -337,6 +405,8 @@ def get_override(sem, xkey, default=None):
def nick_replace( def nick_replace(
department, diplome, rank, modalite, parcours, nick, year=Options.base_year department, diplome, rank, modalite, parcours, nick, year=Options.base_year
): ):
if type(rank) != int:
rank = 0
if len(department) > 0: if len(department) > 0:
nick = nick.replace( nick = nick.replace(
"{department}", conf_value("department_separator") + department "{department}", conf_value("department_separator") + department
@ -357,7 +427,7 @@ def nick_replace(
nick = nick.replace("{diplomenobut}", conf_value("diplome_separator") + diplome) nick = nick.replace("{diplomenobut}", conf_value("diplome_separator") + diplome)
else: else:
nick = nick.replace("{diplomenobut}", "") nick = nick.replace("{diplomenobut}", "")
if len(str(rank)) > 0: if rank > 0:
nick = nick.replace("{rank}", conf_value("rank_separator") + str(rank)) nick = nick.replace("{rank}", conf_value("rank_separator") + str(rank))
else: else:
nick = nick.replace("{rank}", "") nick = nick.replace("{rank}", "")
@ -424,7 +494,6 @@ def analyse_student(semobj, etud, univ_year=None):
modalite = None modalite = None
for g in goal: for g in goal:
gg = g.split("=") gg = g.split("=")
# print(f"Looking for {gg[0]} yielding {gg[1]} out of {groupes}")
if gg[0] in groups: if gg[0] in groups:
modalite = gg[1] modalite = gg[1]
nick = conf_value("nick") nick = conf_value("nick")
@ -523,11 +592,11 @@ def analyse_depts():
continue continue
if bucket in studentsummary["cursus"]: if bucket in studentsummary["cursus"]:
semestreerreur = int(bucket) + 1 semestreerreur = int(bucket) + 1
print( warning(
f"// Élève {etudid} dans deux semestres à la fois : S{semestreerreur}, semestres {studentsummary['cursus'][bucket]} et {semid}" f"// Élève {etudid} dans deux semestres à la fois : S{semestreerreur}, semestres {studentsummary['cursus'][bucket]} et {semid}"
) )
if "dept" in studentsummary and studentsummary["dept"] != dept: if "dept" in studentsummary and studentsummary["dept"] != dept:
print( warning(
f"// Élève ayant changé de département {dept},{studentsummary['dept']}" f"// Élève ayant changé de département {dept},{studentsummary['dept']}"
) )
# department, diplome, rank, modalite, parcours, nick = analyse_student( # department, diplome, rank, modalite, parcours, nick = analyse_student(
@ -1044,7 +1113,8 @@ def crossweight(node_position, node_layer, edges):
return w return w
def genetic_optimize(node_position, node_layer, edges): def genetic_optimize(node_position, node_layer, edges, loops=1):
debug(f"Begin genetic optimization with {loops} loops")
oldcandidates = [] oldcandidates = []
l_indices = list(range(5)) l_indices = list(range(5))
lays = [] lays = []
@ -1055,6 +1125,7 @@ def genetic_optimize(node_position, node_layer, edges):
for i in lays[index]: for i in lays[index]:
randomness_l.append(index) randomness_l.append(index)
w = crossweight(node_position, node_layer, edges) w = crossweight(node_position, node_layer, edges)
for i in range(20): for i in range(20):
oldcandidates.append([node_position.copy(), w]) oldcandidates.append([node_position.copy(), w])
w = crossweight(node_position, node_layer, edges) w = crossweight(node_position, node_layer, edges)
@ -1070,7 +1141,7 @@ def genetic_optimize(node_position, node_layer, edges):
k += 1 k += 1
oldcandidates.append([n, w]) oldcandidates.append([n, w])
candidates = oldcandidates candidates = oldcandidates
for i in range(300): for i in range(loops):
oldcandidates = candidates oldcandidates = candidates
oldcandidates.sort(key=lambda x: x[1]) oldcandidates.sort(key=lambda x: x[1])
candidates = oldcandidates[:30] candidates = oldcandidates[:30]
@ -1119,11 +1190,37 @@ def genetic_optimize(node_position, node_layer, edges):
b = lays[i].copy() b = lays[i].copy()
b.sort(key=lambda x: best[x]) b.sort(key=lambda x: best[x])
orders.append(b) orders.append(b)
print(orders) debug(orders)
print(candidates[0][1]) debug(candidates[0][1])
return orders return orders
def ordernodes(layers, orders, edges):
node_position = {}
node_layer = {}
newls = [[], [], [], [], []]
if orders != {}:
for i in range(len(newls)):
ls = newls[i]
for node in orders[i]:
if node in layers[i]:
ls.append(node)
for node in layers[i]:
if node not in ls:
ls.append(node)
for layer, layernodes in enumerate(newls):
for j, n in enumerate(layernodes):
node_position[n] = j
node_layer[n] = layer
else:
for layer, layernodes in enumerate(layers):
for j, n in enumerate(layernodes):
node_position[n] = j
node_layer[n] = layer
debug(crossweight(node_position, node_layer, edges))
return node_position, node_layer, newls
def printsvg(): def printsvg():
padding = 4 padding = 4
unit_ratio = 96 / 72 unit_ratio = 96 / 72
@ -1164,64 +1261,31 @@ def printsvg():
else: else:
node_structure[startnode]["next"].append([endnode, weight]) node_structure[startnode]["next"].append([endnode, weight])
edges.append([startnode, endnode, weight]) edges.append([startnode, endnode, weight])
node_position = {} filename = "best-" + orderkey
node_layer = {} if Options.restart:
layer_structure = [ try:
{"olayer": []}, os.remove(filename)
{"olayer": []}, except OSError:
{"olayer": []}, pass
{"olayer": []}, if Options.optimize >= 0:
{"olayer": []}, lastorders = read_conf(filename)
]
lastorders = read_conf("best-" + orderkey)
if lastorders != {}:
for i in range(5):
ls = layer_structure[i]
ord = lastorders[i]
for node in lastorders[i]:
if node in layers[i]:
ls["olayer"].append(node)
for node in layers[i]:
if node not in ls["olayer"]:
ls["olayer"].append(node)
for layer, layernodes in enumerate(layer_structure):
for j, n in enumerate(layernodes["olayer"]):
node_position[n] = j
node_layer[n] = layer
print(crossweight(node_position, node_layer, edges))
else: else:
for layer, layernodes in enumerate(layers): lastorders = {}
for j, n in enumerate(layernodes): node_position, node_layer, newls = ordernodes(layers, lastorders, edges)
node_position[n] = j if Options.optimize != 0:
node_layer[n] = layer orders = genetic_optimize(
orders = genetic_optimize(node_position, node_layer, edges) node_position, node_layer, edges, loops=abs(Options.optimize)
)
else:
orders = newls
write_conf("best-" + orderkey, orders) write_conf("best-" + orderkey, orders)
layer_structure = [ node_position, node_layer, newls = ordernodes(layers, orders, edges)
{"olayer": []}, layer_structure = []
{"olayer": []},
{"olayer": []},
{"olayer": []},
{"olayer": []},
]
for i in range(5):
ls = layer_structure[i]
ord = orders[i]
for node in orders[i]:
if node in layers[i]:
ls["olayer"].append(node)
for node in layers[i]:
if node not in ls["olayer"]:
ls["olayer"].append(node)
for layer, layernodes in enumerate(layer_structure):
for j, n in enumerate(layernodes["olayer"]):
node_position[n] = j
node_layer[n] = layer
print(crossweight(node_position, node_layer, edges))
density = [] density = []
for i in range(5): for i in range(5):
ls = layer_structure[i] ls = {}
ls["num"] = len(ls["olayer"]) ls["olayer"] = newls[i]
ls["num"] = len(newls[i])
ls["inout"] = 0 ls["inout"] = 0
for j in ls["olayer"]: for j in ls["olayer"]:
lhi = 0 lhi = 0
@ -1239,6 +1303,7 @@ def printsvg():
print(json.dumps(layer_structure, indent=2)) print(json.dumps(layer_structure, indent=2))
print(json.dumps(node_structure, indent=2)) print(json.dumps(node_structure, indent=2))
ls["inout"] += k["size"] ls["inout"] += k["size"]
layer_structure.append(ls)
if height == 0: if height == 0:
minheight = 0 minheight = 0
for i in range(5): for i in range(5):