forked from ScoDoc/ScoDoc
gen_api_map : commentaire + généralisation
This commit is contained in:
parent
de47277e7c
commit
9b89ca436e
12
scodoc.py
12
scodoc.py
@ -724,7 +724,13 @@ def generate_ens_calendars(): # generate-ens-calendars
|
||||
|
||||
|
||||
@app.cli.command()
|
||||
@click.option(
|
||||
"-e",
|
||||
"--endpoint",
|
||||
default="api",
|
||||
help="Endpoint à partir duquel générer la carte des routes",
|
||||
)
|
||||
@with_appcontext
|
||||
def gen_api_map():
|
||||
"""Show the API map"""
|
||||
tools.gen_api_map(app)
|
||||
def gen_api_map(endpoint):
|
||||
"""Génère la carte des routes de l'API."""
|
||||
tools.gen_api_map(app, endpoint_start=endpoint)
|
||||
|
@ -1,15 +1,48 @@
|
||||
"""
|
||||
Script permettant de générer une carte SVG de l'API de ScoDoc
|
||||
|
||||
Écrit par Matthias HARTMANN
|
||||
"""
|
||||
|
||||
import xml.etree.ElementTree as ET
|
||||
import re
|
||||
|
||||
|
||||
class COLORS:
|
||||
BLUE = "rgb(114,159,207)"
|
||||
GREEN = "rgb(165,214,165)"
|
||||
PINK = "rgb(230,156,190)"
|
||||
GREY = "rgb(224,224,224)"
|
||||
"""
|
||||
Couleurs utilisées pour les éléments de la carte
|
||||
"""
|
||||
|
||||
BLUE = "rgb(114,159,207)" # Couleur de base / élément simple
|
||||
GREEN = "rgb(165,214,165)" # Couleur route GET / valeur query
|
||||
PINK = "rgb(230,156,190)" # Couleur route POST
|
||||
GREY = "rgb(224,224,224)" # Couleur séparateur
|
||||
|
||||
|
||||
class Token:
|
||||
"""
|
||||
Classe permettant de représenter un élément de l'API
|
||||
Exemple :
|
||||
|
||||
/ScoDoc/api/test
|
||||
|
||||
Token(ScoDoc)-> Token(api) -> Token(test)
|
||||
|
||||
Chaque token peut avoir des enfants (Token) et des paramètres de query
|
||||
Chaque token dispose
|
||||
d'un nom (texte écrit dans le rectangle),
|
||||
d'une méthode (GET ou POST) par défaut GET,
|
||||
d'une func_name (nom de la fonction associée à ce token)
|
||||
[OPTIONNEL] d'une query (dictionnaire {param: <type:nom_param>})
|
||||
|
||||
Un token est une leaf si il n'a pas d'enfants.
|
||||
Une LEAF possède un `?` renvoyant vers la doc de la route
|
||||
|
||||
Il est possible de forcer un token à être une pseudo LEAF en mettant force_leaf=True
|
||||
Une PSEUDO LEAF possède aussi un `?` renvoyant vers la doc de la route
|
||||
tout en ayant des enfants.
|
||||
"""
|
||||
|
||||
def __init__(self, name, method="GET", query=None, leaf=False):
|
||||
self.children: list["Token"] = []
|
||||
self.name: str = name
|
||||
@ -19,24 +52,42 @@ class Token:
|
||||
self.func_name = ""
|
||||
|
||||
def add_child(self, child):
|
||||
"""
|
||||
Ajoute un enfant à ce token
|
||||
"""
|
||||
self.children.append(child)
|
||||
|
||||
def find_child(self, name):
|
||||
"""
|
||||
Renvoie l'enfant portant le nom `name` ou None si aucun enfant ne correspond
|
||||
"""
|
||||
for child in self.children:
|
||||
if child.name == name:
|
||||
return child
|
||||
return None
|
||||
|
||||
def __repr__(self, level=0):
|
||||
"""
|
||||
représentation textuelle simplifiée de l'arbre
|
||||
(ne prend pas en compte les query, les méthodes, les func_name, ...)
|
||||
"""
|
||||
ret = "\t" * level + f"({self.name})\n"
|
||||
for child in self.children:
|
||||
ret += child.__repr__(level + 1)
|
||||
return ret
|
||||
|
||||
def is_leaf(self):
|
||||
"""
|
||||
Renvoie True si le token est une leaf, False sinon
|
||||
(i.e. s'il n'a pas d'enfants)
|
||||
(force_leaf n'est pas pris en compte ici)
|
||||
"""
|
||||
return len(self.children) == 0
|
||||
|
||||
def get_height(self, y_step):
|
||||
"""
|
||||
Renvoie la hauteur de l'élément en prenant en compte la hauteur de ses enfants
|
||||
"""
|
||||
# Calculer la hauteur totale des enfants
|
||||
children_height = sum(child.get_height(y_step) for child in self.children)
|
||||
|
||||
@ -51,14 +102,19 @@ class Token:
|
||||
|
||||
def to_svg_group(
|
||||
self,
|
||||
x_offset=0,
|
||||
y_offset=0,
|
||||
x_step=150,
|
||||
y_step=50,
|
||||
parent_coords=None,
|
||||
parent_children_nb=0,
|
||||
x_offset: int = 0,
|
||||
y_offset: int = 0,
|
||||
x_step: int = 150,
|
||||
y_step: int = 50,
|
||||
parent_coords: tuple[tuple[int, int], tuple[int, int]] = None,
|
||||
parent_children_nb: int = 0,
|
||||
):
|
||||
group = ET.Element("g")
|
||||
"""
|
||||
Transforme un token en un groupe SVG
|
||||
(récursif, appelle la fonction sur ses enfants)
|
||||
"""
|
||||
|
||||
group = ET.Element("g") # groupe principal
|
||||
color = COLORS.BLUE
|
||||
if self.is_leaf():
|
||||
if self.method == "GET":
|
||||
@ -66,22 +122,27 @@ class Token:
|
||||
elif self.method == "POST":
|
||||
color = COLORS.PINK
|
||||
|
||||
# Création du rectangle avec le nom du token et placement sur la carte
|
||||
element = _create_svg_element(self.name, color)
|
||||
element.set("transform", f"translate({x_offset}, {y_offset})")
|
||||
group.append(element)
|
||||
|
||||
# On récupère les coordonnées de début et de fin de l'élément pour les flèches
|
||||
current_start_coords, current_end_coords = _get_anchor_coords(
|
||||
element, x_offset, y_offset
|
||||
)
|
||||
# Préparation du lien vers la doc de la route
|
||||
href = "#" + self.func_name.replace("_", "-")
|
||||
if self.query:
|
||||
href += "-query"
|
||||
question_mark_group = _create_question_mark_group(current_end_coords, href)
|
||||
group.append(element)
|
||||
|
||||
# Add an arrow from the parent element to the current element
|
||||
# Ajout de la flèche partant du parent jusqu'à l'élément courant
|
||||
if parent_coords and parent_children_nb > 1:
|
||||
arrow = _create_arrow(parent_coords, current_start_coords)
|
||||
group.append(arrow)
|
||||
|
||||
# Ajout du `/` si le token n'est pas une leaf (ne prend pas en compte force_leaf)
|
||||
if not self.is_leaf():
|
||||
slash_group = _create_svg_element("/", COLORS.GREY)
|
||||
slash_group.set(
|
||||
@ -89,6 +150,7 @@ class Token:
|
||||
f"translate({x_offset + _get_element_width(element)}, {y_offset})",
|
||||
)
|
||||
group.append(slash_group)
|
||||
# Ajout du `?` si le token est une leaf et possède une query
|
||||
if self.is_leaf() and self.query:
|
||||
slash_group = _create_svg_element("?", COLORS.GREY)
|
||||
slash_group.set(
|
||||
@ -96,17 +158,23 @@ class Token:
|
||||
f"translate({x_offset + _get_element_width(element)}, {y_offset})",
|
||||
)
|
||||
group.append(slash_group)
|
||||
|
||||
# Actualisation des coordonnées de fin
|
||||
current_end_coords = _get_anchor_coords(group, 0, 0)[1]
|
||||
|
||||
# Gestion des éléments de la query
|
||||
# Pour chaque élément on va créer :
|
||||
# (param) (=) (valeur) (&)
|
||||
query_y_offset = y_offset
|
||||
query_sub_element = ET.Element("g")
|
||||
for key, value in self.query.items():
|
||||
# Création d'un sous-groupe pour chaque élément de la query
|
||||
sub_group = ET.Element("g")
|
||||
|
||||
# <param>=<value>
|
||||
# On décale l'élément de la query vers la droite par rapport à l'élément parent
|
||||
translate_x = x_offset + x_step
|
||||
|
||||
# <param>
|
||||
# création élément (param)
|
||||
param_el = _create_svg_element(key, COLORS.BLUE)
|
||||
param_el.set(
|
||||
"transform",
|
||||
@ -114,15 +182,16 @@ class Token:
|
||||
)
|
||||
sub_group.append(param_el)
|
||||
|
||||
# add Arrow from "query" to element
|
||||
# Ajout d'une flèche partant de l'élément "query" vers le paramètre courant
|
||||
coords = (
|
||||
current_end_coords,
|
||||
_get_anchor_coords(param_el, translate_x, query_y_offset)[0],
|
||||
)
|
||||
sub_group.append(_create_arrow(*coords))
|
||||
|
||||
# =
|
||||
# création élément (=)
|
||||
equal_el = _create_svg_element("=", COLORS.GREY)
|
||||
# On met à jour le décalage en fonction de l'élément précédent
|
||||
translate_x = x_offset + x_step + _get_element_width(param_el)
|
||||
equal_el.set(
|
||||
"transform",
|
||||
@ -130,8 +199,9 @@ class Token:
|
||||
)
|
||||
sub_group.append(equal_el)
|
||||
|
||||
# <value>
|
||||
# création élément (value)
|
||||
value_el = _create_svg_element(value, COLORS.GREEN)
|
||||
# On met à jour le décalage en fonction des éléments précédents
|
||||
translate_x = (
|
||||
x_offset
|
||||
+ x_step
|
||||
@ -142,9 +212,13 @@ class Token:
|
||||
f"translate({translate_x}, {query_y_offset})",
|
||||
)
|
||||
sub_group.append(value_el)
|
||||
# Si il y a qu'un seul élément dans la query, on ne met pas de `&`
|
||||
if len(self.query) == 1:
|
||||
continue
|
||||
|
||||
# création élément (&)
|
||||
ampersand_group = _create_svg_element("&", "rgb(224,224,224)")
|
||||
# On met à jour le décalage en fonction des éléments précédents
|
||||
translate_x = (
|
||||
x_offset
|
||||
+ x_step
|
||||
@ -156,17 +230,29 @@ class Token:
|
||||
)
|
||||
sub_group.append(ampersand_group)
|
||||
|
||||
# On décale le prochain élément de la query vers le bas
|
||||
query_y_offset += y_step * 1.33
|
||||
|
||||
# On ajoute le sous-groupe (param = value &) au groupe de la query
|
||||
query_sub_element.append(sub_group)
|
||||
|
||||
# On ajoute le groupe de la query à l'élément principal
|
||||
group.append(query_sub_element)
|
||||
|
||||
# Gestion des enfants du Token
|
||||
|
||||
# On met à jour les décalages en fonction des éléments précédents
|
||||
y_offset = query_y_offset
|
||||
current_y_offset = y_offset
|
||||
|
||||
# Pour chaque enfant, on crée un groupe SVG de façon récursive
|
||||
for child in self.children:
|
||||
# On décale l'enfant vers la droite par rapport à l'élément parent
|
||||
# Si il n'y a qu'un enfant, alors on colle l'enfant à l'élément parent
|
||||
rel_x_offset = x_offset + _get_group_width(group)
|
||||
if len(self.children) > 1:
|
||||
rel_x_offset += x_step
|
||||
|
||||
# On crée le groupe SVG de l'enfant
|
||||
child_group = child.to_svg_group(
|
||||
rel_x_offset,
|
||||
current_y_offset,
|
||||
@ -175,10 +261,12 @@ class Token:
|
||||
parent_coords=current_end_coords,
|
||||
parent_children_nb=len(self.children),
|
||||
)
|
||||
# On ajoute le groupe de l'enfant au groupe principal
|
||||
group.append(child_group)
|
||||
# On met à jour le décalage Y en fonction de la hauteur de l'enfant
|
||||
current_y_offset += child.get_height(y_step)
|
||||
|
||||
# add `?` circle a:href to element
|
||||
# Ajout du `?` si le token est une pseudo leaf ou une leaf
|
||||
if self.force_leaf or self.is_leaf():
|
||||
group.append(question_mark_group)
|
||||
|
||||
@ -186,23 +274,32 @@ class Token:
|
||||
|
||||
|
||||
def _create_svg_element(text, color="rgb(230,156,190)"):
|
||||
# Dimensions and styling
|
||||
"""
|
||||
Fonction générale pour créer un élément SVG simple
|
||||
(rectangle avec du texte à l'intérieur)
|
||||
|
||||
text : texte à afficher dans l'élément
|
||||
color : couleur de l'élément
|
||||
"""
|
||||
|
||||
# Paramètres de style de l'élément
|
||||
padding = 5
|
||||
font_size = 16
|
||||
rect_height = 30
|
||||
rect_x = 10
|
||||
rect_y = 20
|
||||
|
||||
# Estimate the text width
|
||||
# Estimation de la largeur du texte
|
||||
text_width = (
|
||||
len(text) * font_size * 0.6
|
||||
) # Estimate based on average character width
|
||||
) # On suppose que la largeur d'un caractère est 0.6 * font_size
|
||||
# Largeur du rectangle = Largeur du texte + padding à gauche et à droite
|
||||
rect_width = text_width + padding * 2
|
||||
|
||||
# Create the SVG group element
|
||||
# Création du groupe SVG
|
||||
group = ET.Element("g")
|
||||
|
||||
# Create the rectangle
|
||||
# Création du rectangle
|
||||
ET.SubElement(
|
||||
group,
|
||||
"rect",
|
||||
@ -215,7 +312,7 @@ def _create_svg_element(text, color="rgb(230,156,190)"):
|
||||
},
|
||||
)
|
||||
|
||||
# Create the text element
|
||||
# Création du texte
|
||||
text_element = ET.SubElement(
|
||||
group,
|
||||
"text",
|
||||
@ -223,47 +320,66 @@ def _create_svg_element(text, color="rgb(230,156,190)"):
|
||||
"x": str(rect_x + padding),
|
||||
"y": str(
|
||||
rect_y + rect_height / 2 + font_size / 2.5
|
||||
), # Adjust to vertically center the text
|
||||
), # Ajustement pour centrer verticalement
|
||||
"font-family": "Courier New, monospace",
|
||||
"font-size": str(font_size),
|
||||
"fill": "black",
|
||||
"style": "white-space: pre;",
|
||||
},
|
||||
)
|
||||
|
||||
# Ajout du texte à l'élément
|
||||
text_element.text = text
|
||||
|
||||
return group
|
||||
|
||||
|
||||
def _get_anchor_coords(element, x_offset, y_offset):
|
||||
"""
|
||||
Récupération des coordonnées des points d'ancrage d'un élément SVG
|
||||
(début et fin de l'élément pour les flèches)
|
||||
(le milieu vertical de l'élément est utilisé pour les flèches)
|
||||
"""
|
||||
bbox = _get_bbox(element, x_offset, y_offset)
|
||||
startX = bbox["x_min"]
|
||||
endX = bbox["x_max"]
|
||||
# Milieu vertical de l'élément
|
||||
y = bbox["y_min"] + (bbox["y_max"] - bbox["y_min"]) / 2
|
||||
return (startX, y), (endX, y)
|
||||
|
||||
|
||||
def _create_arrow(start_coords, end_coords):
|
||||
"""
|
||||
Création d'une flèche entre deux points
|
||||
"""
|
||||
# On récupère les coordonnées de début et de fin de la flèche
|
||||
start_x, start_y = start_coords
|
||||
end_x, end_y = end_coords
|
||||
# On calcule le milieu horizontal de la flèche
|
||||
mid_x = (start_x + end_x) / 2
|
||||
|
||||
# On crée le chemin de la flèche
|
||||
path_data = (
|
||||
f"M {start_x},{start_y} L {mid_x},{start_y} L {mid_x},{end_y} L {end_x},{end_y}"
|
||||
)
|
||||
|
||||
# On crée l'élément path de la flèche
|
||||
path = ET.Element(
|
||||
"path",
|
||||
{
|
||||
"d": path_data,
|
||||
"style": "stroke:black;stroke-width:2;fill:none",
|
||||
"marker-end": "url(#arrowhead)",
|
||||
"marker-end": "url(#arrowhead)", # Ajout de la flèche à la fin du path
|
||||
},
|
||||
)
|
||||
return path
|
||||
|
||||
|
||||
def _get_element_width(element):
|
||||
"""
|
||||
Retourne la largueur d'un élément simple
|
||||
L'élément simple correspond à un rectangle avec du texte à l'intérieur
|
||||
on récupère la largueur du rectangle
|
||||
"""
|
||||
rect = element.find("rect")
|
||||
if rect is not None:
|
||||
return float(rect.get("width", 0))
|
||||
@ -271,18 +387,27 @@ def _get_element_width(element):
|
||||
|
||||
|
||||
def _get_group_width(group):
|
||||
"""
|
||||
Récupère la largeur d'un groupe d'éléments
|
||||
on fait la somme des largeurs de chaque élément du groupe
|
||||
"""
|
||||
return sum(_get_element_width(child) for child in group)
|
||||
|
||||
|
||||
def _create_question_mark_group(coords, href):
|
||||
"""
|
||||
Création d'un groupe SVG contenant un cercle et un lien vers la doc de la route
|
||||
le `?` renvoie vers la doc de la route
|
||||
"""
|
||||
# Récupération du point d'ancrage de l'élément
|
||||
x, y = coords
|
||||
radius = 10 # Radius of the circle
|
||||
radius = 10 # Rayon du cercle
|
||||
y -= radius * 2
|
||||
font_size = 17 # Font size of the question mark
|
||||
font_size = 17 # Taille de la police
|
||||
|
||||
group = ET.Element("g")
|
||||
|
||||
# Create the circle
|
||||
# Création du cercle
|
||||
ET.SubElement(
|
||||
group,
|
||||
"circle",
|
||||
@ -296,17 +421,17 @@ def _create_question_mark_group(coords, href):
|
||||
},
|
||||
)
|
||||
|
||||
# Create the link element
|
||||
# Création du lien (a) vers la doc de la route
|
||||
link = ET.Element("a", {"href": href})
|
||||
|
||||
# Create the text element
|
||||
# Création du texte `?`
|
||||
text_element = ET.SubElement(
|
||||
link,
|
||||
"text",
|
||||
{
|
||||
"x": str(x + 1),
|
||||
"y": str(y + font_size / 3), # Adjust to vertically center the text
|
||||
"text-anchor": "middle", # Center the text horizontally
|
||||
"y": str(y + font_size / 3), # Ajustement pour centrer verticalement
|
||||
"text-anchor": "middle", # Centrage horizontal
|
||||
"font-family": "Arial",
|
||||
"font-size": str(font_size),
|
||||
"fill": "black",
|
||||
@ -319,23 +444,49 @@ def _create_question_mark_group(coords, href):
|
||||
return group
|
||||
|
||||
|
||||
def gen_api_map(app):
|
||||
def gen_api_map(app, endpoint_start="api"):
|
||||
"""
|
||||
Fonction permettant de générer une carte SVG de l'API de ScoDoc
|
||||
Elle récupère les routes de l'API et les transforme en un arbre de Token
|
||||
puis génère un fichier SVG à partir de cet arbre
|
||||
"""
|
||||
# Création du token racine
|
||||
api_map = Token("")
|
||||
|
||||
# Parcours de toutes les routes de l'application
|
||||
for rule in app.url_map.iter_rules():
|
||||
# On ne garde que les routes de l'API / APIWEB
|
||||
if not rule.endpoint.lower().startswith("api"):
|
||||
if not rule.endpoint.lower().startswith(endpoint_start.lower()):
|
||||
continue
|
||||
|
||||
# Transformation de la route en segments
|
||||
# ex : /ScoDoc/api/test -> ["ScoDoc", "api", "test"]
|
||||
segments = rule.rule.strip("/").split("/")
|
||||
|
||||
# On positionne le token courant sur le token racine
|
||||
current_token = api_map
|
||||
|
||||
# Pour chaque segment de la route
|
||||
for i, segment in enumerate(segments):
|
||||
# Check if the segment already exists in the current level
|
||||
# On cherche si le segment est déjà un enfant du token courant
|
||||
child = current_token.find_child(segment)
|
||||
|
||||
# Si ce n'est pas le cas on crée un nouveau token et on l'ajoute comme enfant
|
||||
if child is None:
|
||||
func = app.view_functions[rule.endpoint]
|
||||
# If it's the last segment
|
||||
# Si c'est le dernier segment, on marque le token comme une leaf
|
||||
# On utilise force_leaf car il est possible que le token ne soit que
|
||||
# momentanément une leaf
|
||||
# ex :
|
||||
# - /ScoDoc/api/test/ -> ["ScoDoc", "api", "test"]
|
||||
# - /ScoDoc/api/test/1 -> ["ScoDoc", "api", "test", "1"]
|
||||
# dans le premier cas test est une leaf, dans le deuxième cas test n'est pas une leaf
|
||||
# force_leaf permet de forcer le token à être une leaf même s'il a des enfants
|
||||
# permettant d'afficher le `?` renvoyant vers la doc de la route
|
||||
# car la route peut être utilisée sans forcément la continuer.
|
||||
|
||||
if i == len(segments) - 1:
|
||||
# Un Token sera query si parse_query_doc retourne un dictionnaire non vide
|
||||
child = Token(
|
||||
segment,
|
||||
leaf=True,
|
||||
@ -345,16 +496,18 @@ def gen_api_map(app):
|
||||
child = Token(
|
||||
segment,
|
||||
)
|
||||
|
||||
# On ajoute le token comme enfant du token courant
|
||||
# en donnant la méthode et le nom de la fonction associée
|
||||
child.func_name = func.__name__
|
||||
method: str = "POST" if "POST" in rule.methods else "GET"
|
||||
child.method = method
|
||||
current_token.add_child(child)
|
||||
|
||||
# On met à jour le token courant pour le prochain segment
|
||||
current_token = child
|
||||
|
||||
# Mark the last segment as a leaf node if it's not already marked
|
||||
if not current_token.is_leaf():
|
||||
current_token.force_leaf = True
|
||||
|
||||
# On génère le SVG à partir de l'arbre de Token
|
||||
generate_svg(api_map.to_svg_group(), "/tmp/api_map.svg")
|
||||
print(
|
||||
"La carte a été générée avec succès. "
|
||||
@ -363,7 +516,12 @@ def gen_api_map(app):
|
||||
|
||||
|
||||
def _get_bbox(element, x_offset=0, y_offset=0):
|
||||
# Helper function to calculate the bounding box of an SVG element
|
||||
"""
|
||||
Récupérer les coordonnées de la boîte englobante d'un élément SVG
|
||||
Utilisé pour calculer les coordonnées d'un élément SVG et pour avoir la taille
|
||||
total du SVG
|
||||
"""
|
||||
# Initialisation des coordonnées de la boîte englobante
|
||||
bbox = {
|
||||
"x_min": float("inf"),
|
||||
"y_min": float("inf"),
|
||||
@ -371,17 +529,26 @@ def _get_bbox(element, x_offset=0, y_offset=0):
|
||||
"y_max": float("-inf"),
|
||||
}
|
||||
|
||||
# Parcours récursif des enfants de l'élément
|
||||
for child in element:
|
||||
# On récupère la transformation (position) de l'enfant
|
||||
# On met les OffSet par défaut à leur valeur donnée en paramètre
|
||||
transform = child.get("transform")
|
||||
child_x_offset = x_offset
|
||||
child_y_offset = y_offset
|
||||
|
||||
# Si la transformation est définie, on récupère les coordonnées de translation
|
||||
# et on les ajoute aux offsets
|
||||
if transform:
|
||||
translate = transform.replace("translate(", "").replace(")", "").split(",")
|
||||
if len(translate) == 2:
|
||||
child_x_offset += float(translate[0])
|
||||
child_y_offset += float(translate[1])
|
||||
|
||||
# On regarde ensuite la boite englobante de l'enfant
|
||||
# On met à jour les coordonnées de la boîte englobante en fonction de l'enfant
|
||||
# x_min, y_min, x_max, y_max.
|
||||
|
||||
if child.tag == "rect":
|
||||
x = child_x_offset + float(child.get("x", 0))
|
||||
y = child_y_offset + float(child.get("y", 0))
|
||||
@ -403,10 +570,16 @@ def _get_bbox(element, x_offset=0, y_offset=0):
|
||||
|
||||
|
||||
def generate_svg(element, fname):
|
||||
"""
|
||||
Génère un fichier SVG à partir d'un élément SVG
|
||||
"""
|
||||
# On récupère les dimensions de l'élément racine
|
||||
bbox = _get_bbox(element)
|
||||
width = bbox["x_max"] - bbox["x_min"] + 80 # Add some padding
|
||||
height = bbox["y_max"] - bbox["y_min"] + 80 # Add some padding
|
||||
# On définit la taille du SVG en fonction des dimensions de l'élément racine
|
||||
width = bbox["x_max"] - bbox["x_min"] + 80
|
||||
height = bbox["y_max"] - bbox["y_min"] + 80
|
||||
|
||||
# Création de l'élément racine du SVG
|
||||
svg = ET.Element(
|
||||
"svg",
|
||||
{
|
||||
@ -417,7 +590,8 @@ def generate_svg(element, fname):
|
||||
},
|
||||
)
|
||||
|
||||
# Define the marker for the arrowhead
|
||||
# Création du motif de la flèche pour les liens
|
||||
# (définition d'un marqueur pour les flèches)
|
||||
defs = ET.SubElement(svg, "defs")
|
||||
marker = ET.SubElement(
|
||||
defs,
|
||||
@ -433,8 +607,10 @@ def generate_svg(element, fname):
|
||||
)
|
||||
ET.SubElement(marker, "polygon", {"points": "0 0, 10 3.5, 0 7"})
|
||||
|
||||
# Ajout de l'élément principal à l'élément racine
|
||||
svg.append(element)
|
||||
|
||||
# Écriture du fichier SVG
|
||||
tree = ET.ElementTree(svg)
|
||||
tree.write(fname, encoding="utf-8", xml_declaration=True)
|
||||
|
||||
@ -454,30 +630,39 @@ def parse_query_doc(doc):
|
||||
Dès qu'une ligne ne respecte pas ce format (voir regex dans la fonction), on arrête de parser
|
||||
Attention, la ligne ----- doit être collée contre QUERY et contre le premier paramètre
|
||||
"""
|
||||
|
||||
# Récupérer les lignes de la doc
|
||||
lines = [line.strip() for line in doc.split("\n")]
|
||||
# On cherche la ligne "QUERY" et on vérifie que la ligne suivante est "-----"
|
||||
# Si ce n'est pas le cas, on renvoie un dictionnaire vide
|
||||
try:
|
||||
query_index = lines.index("QUERY")
|
||||
if lines[query_index + 1] != "-----":
|
||||
return {}
|
||||
except ValueError:
|
||||
return {}
|
||||
|
||||
# On récupère les lignes de la doc qui correspondent à la query (enfin on espère)
|
||||
query_lines = lines[query_index + 2 :]
|
||||
|
||||
query = {}
|
||||
regex = re.compile(r"^(\w+):(<.+>)$")
|
||||
for line in query_lines:
|
||||
# On verifie que la ligne respecte le format attendu
|
||||
# Si ce n'est pas le cas, on arrête de parser
|
||||
parts = regex.match(line)
|
||||
if not parts:
|
||||
break
|
||||
# On récupère le paramètre et son type:nom
|
||||
param, type_nom_param = parts.groups()
|
||||
# On ajoute le paramètre au dictionnaire
|
||||
query[param] = type_nom_param
|
||||
|
||||
return query
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Exemple d'utilisation de la classe Token
|
||||
# Exemple simple de création d'un arbre de Token
|
||||
|
||||
root = Token("api")
|
||||
child1 = Token("assiduites", leaf=True)
|
||||
child1.func_name = "assiduites_get"
|
||||
|
Loading…
Reference in New Issue
Block a user