forked from ScoDoc/ScoDoc
Génération tableau API
This commit is contained in:
parent
36547afb0b
commit
9ca86e7900
@ -19,7 +19,8 @@ import app.scodoc.sco_assiduites as scass
|
|||||||
import app.scodoc.sco_utils as scu
|
import app.scodoc.sco_utils as scu
|
||||||
from app.api import api_bp as bp
|
from app.api import api_bp as bp
|
||||||
from app.api import api_web_bp, get_model_api_object, tools
|
from app.api import api_web_bp, get_model_api_object, tools
|
||||||
from app.decorators import permission_required, scodoc
|
from app.api import api_permission_required as permission_required
|
||||||
|
from app.decorators import scodoc
|
||||||
from app.models import (
|
from app.models import (
|
||||||
Assiduite,
|
Assiduite,
|
||||||
Evaluation,
|
Evaluation,
|
||||||
|
@ -7,6 +7,8 @@ Script permettant de générer une carte SVG de l'API de ScoDoc
|
|||||||
import xml.etree.ElementTree as ET
|
import xml.etree.ElementTree as ET
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
from app.auth.models import Permission
|
||||||
|
|
||||||
|
|
||||||
class COLORS:
|
class COLORS:
|
||||||
"""
|
"""
|
||||||
@ -437,15 +439,20 @@ def _create_question_mark_group(coords, href):
|
|||||||
return group
|
return group
|
||||||
|
|
||||||
|
|
||||||
|
# point d'entrée de la commande `flask gen-api-map`
|
||||||
def gen_api_map(app, endpoint_start="api"):
|
def gen_api_map(app, endpoint_start="api"):
|
||||||
"""
|
"""
|
||||||
Fonction permettant de générer une carte SVG de l'API de ScoDoc
|
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
|
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
|
puis génère un fichier SVG à partir de cet arbre
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
print("DEBUG", app.view_functions["apiweb.user_info"].scodoc_permission)
|
||||||
# Création du token racine
|
# Création du token racine
|
||||||
api_map = Token("")
|
api_map = Token("")
|
||||||
|
|
||||||
|
doctable_lines: dict[str, dict] = {}
|
||||||
|
|
||||||
# Parcours de toutes les routes de l'application
|
# Parcours de toutes les routes de l'application
|
||||||
for rule in app.url_map.iter_rules():
|
for rule in app.url_map.iter_rules():
|
||||||
# On ne garde que les routes de l'API / APIWEB
|
# On ne garde que les routes de l'API / APIWEB
|
||||||
@ -500,6 +507,28 @@ def gen_api_map(app, endpoint_start="api"):
|
|||||||
child.method = method
|
child.method = method
|
||||||
current_token.add_child(child)
|
current_token.add_child(child)
|
||||||
|
|
||||||
|
# Gestion de doctable
|
||||||
|
doctable = parse_doctable_doc(func.__doc__ or "")
|
||||||
|
href = func_name.replace("_", "-")
|
||||||
|
if child.query and not href.endswith("-query"):
|
||||||
|
href += "-query"
|
||||||
|
|
||||||
|
permissions: str
|
||||||
|
try:
|
||||||
|
permissions: str = ", ".join(
|
||||||
|
sorted(Permission.permissions_names(func.scodoc_permission))
|
||||||
|
)
|
||||||
|
except AttributeError:
|
||||||
|
permissions = "Aucune permission requise"
|
||||||
|
|
||||||
|
doctable_lines[func_name] = {
|
||||||
|
"doctable": doctable,
|
||||||
|
"method": method,
|
||||||
|
"nom": func_name,
|
||||||
|
"href": href,
|
||||||
|
"permission": permissions,
|
||||||
|
}
|
||||||
|
|
||||||
# On met à jour le token courant pour le prochain segment
|
# On met à jour le token courant pour le prochain segment
|
||||||
current_token = child
|
current_token = child
|
||||||
|
|
||||||
@ -510,6 +539,9 @@ def gen_api_map(app, endpoint_start="api"):
|
|||||||
+ "Vous pouvez la consulter à l'adresse suivante : /tmp/api_map.svg"
|
+ "Vous pouvez la consulter à l'adresse suivante : /tmp/api_map.svg"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# On génère le tableau à partir de doctable_lines
|
||||||
|
_gen_table(sorted(doctable_lines.values(), key=lambda x: x["nom"]))
|
||||||
|
|
||||||
|
|
||||||
def _get_bbox(element, x_offset=0, y_offset=0):
|
def _get_bbox(element, x_offset=0, y_offset=0):
|
||||||
"""
|
"""
|
||||||
@ -617,6 +649,7 @@ def generate_svg(element, fname):
|
|||||||
def _get_doc_lines(keyword, doc) -> list[str]:
|
def _get_doc_lines(keyword, doc) -> list[str]:
|
||||||
"""
|
"""
|
||||||
Renvoie les lignes de la doc qui suivent le mot clé keyword
|
Renvoie les lignes de la doc qui suivent le mot clé keyword
|
||||||
|
Attention : s'arrête à la première ligne vide
|
||||||
|
|
||||||
La doc doit contenir des lignes de la forme:
|
La doc doit contenir des lignes de la forme:
|
||||||
|
|
||||||
@ -638,10 +671,18 @@ def _get_doc_lines(keyword, doc) -> list[str]:
|
|||||||
return []
|
return []
|
||||||
# On récupère les lignes de la doc qui correspondent au keyword (enfin on espère)
|
# On récupère les lignes de la doc qui correspondent au keyword (enfin on espère)
|
||||||
kw_lines = lines[kw_index + 2 :]
|
kw_lines = lines[kw_index + 2 :]
|
||||||
|
|
||||||
|
# On s'arrête à la première ligne vide
|
||||||
|
first_empty_line: int
|
||||||
|
try:
|
||||||
|
first_empty_line: int = kw_lines.index("")
|
||||||
|
except ValueError:
|
||||||
|
first_empty_line = len(kw_lines)
|
||||||
|
kw_lines = kw_lines[:first_empty_line]
|
||||||
return kw_lines
|
return kw_lines
|
||||||
|
|
||||||
|
|
||||||
def parse_doc_name(doc):
|
def parse_doc_name(doc) -> str:
|
||||||
"""
|
"""
|
||||||
renvoie le nom de la route à partir de la docstring
|
renvoie le nom de la route à partir de la docstring
|
||||||
|
|
||||||
@ -658,7 +699,7 @@ def parse_doc_name(doc):
|
|||||||
return name_lines[0] if name_lines else None
|
return name_lines[0] if name_lines else None
|
||||||
|
|
||||||
|
|
||||||
def parse_query_doc(doc):
|
def parse_query_doc(doc) -> dict[str, str]:
|
||||||
"""
|
"""
|
||||||
renvoie un dictionnaire {param: <type:nom_param>} (ex: {assiduite_id : <int:assiduite_id>})
|
renvoie un dictionnaire {param: <type:nom_param>} (ex: {assiduite_id : <int:assiduite_id>})
|
||||||
|
|
||||||
@ -691,33 +732,125 @@ def parse_query_doc(doc):
|
|||||||
return query
|
return query
|
||||||
|
|
||||||
|
|
||||||
|
def parse_doctable_doc(doc) -> dict[str, str]:
|
||||||
|
"""
|
||||||
|
Retourne un dictionnaire représentant les informations du tableau d'api
|
||||||
|
à partir de la doc (DOC-TABLE)
|
||||||
|
|
||||||
|
éléments optionnels:
|
||||||
|
- `permissions` permissions nécessaires pour accéder à la route (ScoView, AbsChange, ...)
|
||||||
|
- `href` nom (sans #) de l'ancre dans la page ScoDoc9API
|
||||||
|
|
||||||
|
DOC-TABLE
|
||||||
|
---------
|
||||||
|
permissions: ScoView
|
||||||
|
href: une-fonction
|
||||||
|
"""
|
||||||
|
|
||||||
|
doc_lines: list[str] = _get_doc_lines("DOC-TABLE", doc)
|
||||||
|
|
||||||
|
# On crée un dictionnaire
|
||||||
|
table = {}
|
||||||
|
|
||||||
|
# on parcourt les lignes de la doc
|
||||||
|
for line in doc_lines:
|
||||||
|
# On sépare le paramètre et sa valeur
|
||||||
|
param, value = line.split(":")
|
||||||
|
# On met à jour le dictionnaire
|
||||||
|
table[param.strip()] = value.strip()
|
||||||
|
|
||||||
|
return table
|
||||||
|
|
||||||
|
|
||||||
|
def _gen_table_line(nom, href, method, permission, doctable: dict):
|
||||||
|
"""
|
||||||
|
Génère une ligne de tableau markdown
|
||||||
|
|
||||||
|
| nom de la route| methode HTTP| Permission |
|
||||||
|
"""
|
||||||
|
lien: str = href
|
||||||
|
if "href" in doctable:
|
||||||
|
lien: str = doctable.get("href")
|
||||||
|
nav: str = f"[{nom}]({'#'+lien})"
|
||||||
|
|
||||||
|
table: str = "|"
|
||||||
|
for string in [nav, method, doctable.get("permissions") or permission]:
|
||||||
|
table += f" {string} |"
|
||||||
|
|
||||||
|
return table
|
||||||
|
|
||||||
|
|
||||||
|
def _gen_table_head() -> str:
|
||||||
|
"""
|
||||||
|
Génère la première ligne du tableau markdown
|
||||||
|
"""
|
||||||
|
|
||||||
|
headers: str = "| Route | Méthode | Permission |"
|
||||||
|
line: str = "|---|---|---|"
|
||||||
|
|
||||||
|
return f"{headers}\n{line}\n"
|
||||||
|
|
||||||
|
|
||||||
|
def _gen_table(lines: list[dict], filename: str = "/tmp/api_table.md") -> str:
|
||||||
|
"""
|
||||||
|
Génère un tableau markdown à partir d'une liste de lignes
|
||||||
|
|
||||||
|
lines : liste de dictionnaire au format :
|
||||||
|
|
||||||
|
- doctable : dict généré par parse_doctable_doc
|
||||||
|
- nom : nom de la fonction associée à la route
|
||||||
|
- method : GET ou POST
|
||||||
|
- permission : Permissions de la route (auto récupérée)
|
||||||
|
|
||||||
|
"""
|
||||||
|
table = _gen_table_head()
|
||||||
|
table += "\n".join([_gen_table_line(**line) for line in lines])
|
||||||
|
|
||||||
|
with open(filename, "w", encoding="UTF-8") as f:
|
||||||
|
f.write(table)
|
||||||
|
|
||||||
|
print(
|
||||||
|
f"Le tableau a été généré avec succès. Vous pouvez le consulter à l'adresse suivante : {filename}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
# Exemple d'utilisation de la classe Token
|
# Exemple d'utilisation de la classe Token
|
||||||
# Exemple simple de création d'un arbre de Token
|
# Exemple simple de création d'un arbre de Token
|
||||||
|
|
||||||
root = Token("api")
|
# root = Token("api")
|
||||||
child1 = Token("assiduites", leaf=True)
|
# child1 = Token("assiduites", leaf=True)
|
||||||
child1.func_name = "assiduites_get"
|
# child1.func_name = "assiduites_get"
|
||||||
child2 = Token("count")
|
# child2 = Token("count")
|
||||||
child22 = Token("all")
|
# child22 = Token("all")
|
||||||
child23 = Token(
|
# child23 = Token(
|
||||||
"query",
|
# "query",
|
||||||
query={
|
# query={
|
||||||
"etat": "<string:etat>",
|
# "etat": "<string:etat>",
|
||||||
"moduleimpl_id": "<int:moduleimpl_id>",
|
# "moduleimpl_id": "<int:moduleimpl_id>",
|
||||||
"count": "<int:count>",
|
# "count": "<int:count>",
|
||||||
"formsemestre_id": "<int:formsemestre_id>",
|
# "formsemestre_id": "<int:formsemestre_id>",
|
||||||
},
|
# },
|
||||||
)
|
# )
|
||||||
child3 = Token("justificatifs", "POST")
|
# child3 = Token("justificatifs", "POST")
|
||||||
child3.func_name = "justificatifs_post"
|
# child3.func_name = "justificatifs_post"
|
||||||
|
|
||||||
root.add_child(child1)
|
# root.add_child(child1)
|
||||||
child1.add_child(child2)
|
# child1.add_child(child2)
|
||||||
child2.add_child(child22)
|
# child2.add_child(child22)
|
||||||
child2.add_child(child23)
|
# child2.add_child(child23)
|
||||||
root.add_child(child3)
|
# root.add_child(child3)
|
||||||
|
|
||||||
group_element = root.to_svg_group()
|
# group_element = root.to_svg_group()
|
||||||
|
|
||||||
generate_svg(group_element, "/tmp/api_map.svg")
|
# generate_svg(group_element, "/tmp/api_map.svg")
|
||||||
|
dt: dict = parse_doctable_doc(parse_doctable_doc.__doc__)
|
||||||
|
md: str = "POST"
|
||||||
|
hf: str = "assiduites-query"
|
||||||
|
|
||||||
|
doc: dict = {
|
||||||
|
"doctable": dt,
|
||||||
|
"method": md,
|
||||||
|
"href": hf,
|
||||||
|
}
|
||||||
|
print(_gen_table([doc]))
|
||||||
|
Loading…
Reference in New Issue
Block a user