209 lines
9.0 KiB
Python
209 lines
9.0 KiB
Python
import streamlit as st
|
|
import requests
|
|
import re
|
|
from config import GITEA_TOKEN
|
|
from utils.gitea import charger_arborescence_fiches
|
|
from utils.tickets_fiche import gerer_tickets_fiche
|
|
import markdown
|
|
from bs4 import BeautifulSoup
|
|
from latex2mathml.converter import convert as latex_to_mathml
|
|
from utils.fiche_utils import load_seuils, render_fiche_markdown
|
|
from utils.fiche_dynamic import build_dynamic_sections, build_ivc_sections, build_ihh_sections, build_isg_sections, build_assemblage_sections
|
|
import os
|
|
from utils.gitea import recuperer_date_dernier_commit
|
|
from datetime import datetime, timezone
|
|
import yaml
|
|
|
|
from config import GITEA_URL, ORGANISATION, DEPOT_FICHES, ENV
|
|
|
|
def remplacer_latex_par_mathml(markdown_text):
|
|
def remplacer_bloc_display(match):
|
|
formule_latex = match.group(1).strip()
|
|
try:
|
|
mathml = latex_to_mathml(formule_latex, display='block')
|
|
return f'<div class="math-block">{mathml}</div>'
|
|
except Exception as e:
|
|
return f"<pre>Erreur LaTeX block: {e}</pre>"
|
|
|
|
def remplacer_bloc_inline(match):
|
|
formule_latex = match.group(1).strip()
|
|
try:
|
|
mathml = latex_to_mathml(formule_latex, display='inline')
|
|
return f'<span class="math-inline">{mathml}</span>'
|
|
except Exception as e:
|
|
return f"<code>Erreur LaTeX inline: {e}</code>"
|
|
|
|
# Important : d'abord les $$...$$, ensuite les $...$
|
|
markdown_text = re.sub(r"\$\$(.*?)\$\$", remplacer_bloc_display, markdown_text, flags=re.DOTALL)
|
|
markdown_text = re.sub(r"(?<!\$)\$(.+?)\$(?!\$)", remplacer_bloc_inline, markdown_text, flags=re.DOTALL)
|
|
return markdown_text
|
|
|
|
def markdown_to_html_rgaa(markdown_text, caption_text=None):
|
|
html = markdown.markdown(markdown_text, extensions=['tables'])
|
|
soup = BeautifulSoup(html, "html.parser")
|
|
for i, table in enumerate(soup.find_all("table"), start=1):
|
|
table["role"] = "table"
|
|
table["summary"] = caption_text
|
|
if caption_text:
|
|
caption = soup.new_tag("caption")
|
|
caption.string = caption_text
|
|
table.insert(len(table.contents), caption)
|
|
for th in table.find_all("th"):
|
|
th["scope"] = "col"
|
|
return str(soup)
|
|
|
|
|
|
|
|
def creer_fiche(md_source: str, dossier: str, nom_fichier: str, seuils: dict) -> str:
|
|
# Extraire bloc YAML d'en-tête
|
|
front_match = re.match(r"(?s)^---\n(.*?)\n---\n", md_source)
|
|
context = {}
|
|
if front_match:
|
|
context = yaml.safe_load(front_match.group(1))
|
|
#md_source = md_source[front_match.end():] # retirer le front-matter du corps
|
|
|
|
# Traitement conditionnel selon type
|
|
type_fiche = context.get("type_fiche")
|
|
if type_fiche == "indice":
|
|
if context.get("indice_court") == "ICS":
|
|
md_source = build_dynamic_sections(md_source)
|
|
elif context.get("indice_court") == "IVC":
|
|
md_source = build_ivc_sections(md_source)
|
|
elif context.get("indice_court") == "IHH":
|
|
md_source = build_ihh_sections(md_source)
|
|
elif context.get("indice_court") == "ISG":
|
|
md_source = build_isg_sections(md_source)
|
|
elif type_fiche == "assemblage" or type_fiche == "fabrication":
|
|
md_source = build_assemblage_sections(md_source)
|
|
|
|
# Rendu markdown principal
|
|
contenu_md = render_fiche_markdown(md_source, seuils)
|
|
|
|
#st.code(md_source)
|
|
#md_pairs = build_dynamic_sections(md_source)
|
|
#contenu_md = render_fiche_markdown(md_pairs, seuils)
|
|
|
|
# Sauvegarde .md
|
|
md_path = os.path.join("Fiches", dossier, nom_fichier)
|
|
os.makedirs(os.path.dirname(md_path), exist_ok=True)
|
|
with open(md_path, "w", encoding="utf-8") as f:
|
|
f.write(contenu_md)
|
|
|
|
# Traitement en sections
|
|
lignes = contenu_md.split('\n')
|
|
sections_n1 = []
|
|
section_n1_actuelle = {"titre": None, "intro": [], "sections_n2": {}}
|
|
dans_section_n1 = False
|
|
section_n2_actuelle = None
|
|
|
|
for ligne in lignes:
|
|
if re.match(r'^#[^#]', ligne):
|
|
if section_n1_actuelle["titre"] or section_n1_actuelle["intro"] or section_n1_actuelle["sections_n2"]:
|
|
sections_n1.append(section_n1_actuelle)
|
|
section_n1_actuelle = {
|
|
"titre": ligne.strip('# ').strip(),
|
|
"intro": [],
|
|
"sections_n2": {}
|
|
}
|
|
section_n2_actuelle = None
|
|
dans_section_n1 = True
|
|
elif re.match(r'^##[^#]', ligne):
|
|
section_n2_actuelle = ligne.strip('# ').strip()
|
|
section_n1_actuelle["sections_n2"][section_n2_actuelle] = [f"## {section_n2_actuelle}"]
|
|
elif section_n2_actuelle:
|
|
section_n1_actuelle["sections_n2"][section_n2_actuelle].append(ligne)
|
|
elif dans_section_n1:
|
|
section_n1_actuelle["intro"].append(ligne)
|
|
|
|
if section_n1_actuelle["titre"] or section_n1_actuelle["intro"] or section_n1_actuelle["sections_n2"]:
|
|
sections_n1.append(section_n1_actuelle)
|
|
|
|
# Génération HTML
|
|
bloc_titre = sections_n1[0]["titre"] if sections_n1 and sections_n1[0]["titre"] else "fiche"
|
|
titre_id = re.sub(r'\W+', '-', bloc_titre.lower()).strip('-')
|
|
|
|
html_output = [f'<section role="region" aria-labelledby="{titre_id}">',
|
|
f'<h1 id="{titre_id}">{sections_n1[0]["titre"]}</h1>']
|
|
|
|
for bloc in sections_n1:
|
|
if bloc["titre"] and bloc["titre"] != sections_n1[0]["titre"]:
|
|
html_output.append(f"<h2>{bloc['titre']}</h2>")
|
|
|
|
if bloc["intro"]:
|
|
intro_md = remplacer_latex_par_mathml("\n".join(bloc["intro"]))
|
|
html_intro = markdown_to_html_rgaa(intro_md, caption_text=None)
|
|
html_output.append(html_intro)
|
|
|
|
for sous_titre, contenu in bloc["sections_n2"].items():
|
|
contenu_md = remplacer_latex_par_mathml("\n".join(contenu))
|
|
contenu_html = markdown_to_html_rgaa(contenu_md, caption_text=sous_titre)
|
|
html_output.append(f"<details><summary>{sous_titre}</summary>{contenu_html}</details>")
|
|
|
|
html_output.append("</section>")
|
|
|
|
html_dir = os.path.join("HTML", dossier)
|
|
os.makedirs(html_dir, exist_ok=True)
|
|
html_path = os.path.join(html_dir, os.path.splitext(nom_fichier)[0] + ".html")
|
|
|
|
with open(html_path, "w", encoding="utf-8") as f:
|
|
f.write("\n".join(html_output))
|
|
|
|
return html_path
|
|
|
|
def afficher_fiches():
|
|
if "fiches_arbo" not in st.session_state:
|
|
st.session_state["fiches_arbo"] = charger_arborescence_fiches()
|
|
|
|
arbo = st.session_state.get("fiches_arbo", {})
|
|
if not arbo:
|
|
st.warning("Aucune fiche disponible pour le moment.")
|
|
return
|
|
|
|
dossiers = sorted(arbo.keys(), key=lambda x: x.lower())
|
|
dossier_choisi = st.selectbox("Choisissez un dossier", ["-- Sélectionner un dossier --"] + dossiers)
|
|
|
|
if dossier_choisi and dossier_choisi != "-- Sélectionner un dossier --":
|
|
fiches = arbo.get(dossier_choisi, [])
|
|
noms_fiches = [f['nom'] for f in fiches]
|
|
|
|
fiche_choisie = st.selectbox("Choisissez une fiche", ["-- Sélectionner une fiche --"] + noms_fiches)
|
|
|
|
if fiche_choisie and fiche_choisie != "-- Sélectionner une fiche --":
|
|
fiche_info = next((f for f in fiches if f["nom"] == fiche_choisie), None)
|
|
if fiche_info:
|
|
try:
|
|
headers = {"Authorization": f"token {GITEA_TOKEN}"}
|
|
reponse_fiche = requests.get(fiche_info["download_url"], headers=headers)
|
|
reponse_fiche.raise_for_status()
|
|
md_source = reponse_fiche.text
|
|
|
|
if "seuils" not in st.session_state:
|
|
SEUILS = load_seuils("assets/config.yaml")
|
|
st.session_state["seuils"] = SEUILS
|
|
elif "seuils" in st.session_state:
|
|
SEUILS = st.session_state["seuils"]
|
|
|
|
html_path = os.path.join("HTML", dossier_choisi, os.path.splitext(fiche_choisie)[0] + ".html")
|
|
path_relative = f"Documents/{dossier_choisi}/{fiche_choisie}"
|
|
commits_url = f"{GITEA_URL}/repos/{ORGANISATION}/{DEPOT_FICHES}/commits?path={path_relative}&sha={ENV}"
|
|
|
|
regenerate = True
|
|
if os.path.exists(html_path):
|
|
remote_last_modified = recuperer_date_dernier_commit(commits_url)
|
|
local_last_modified = datetime.fromtimestamp(os.path.getmtime(html_path), tz=timezone.utc)
|
|
regenerate = not remote_last_modified or remote_last_modified > local_last_modified
|
|
|
|
if regenerate:
|
|
st.info("DEBUG : Régénération de la fiche")
|
|
html_path = creer_fiche(md_source, dossier_choisi, fiche_choisie, SEUILS)
|
|
else:
|
|
st.info("DEBUG : Pas de régénération")
|
|
|
|
with open(html_path, "r", encoding="utf-8") as f:
|
|
st.markdown(f.read(), unsafe_allow_html=True)
|
|
|
|
gerer_tickets_fiche(fiche_choisie)
|
|
|
|
except Exception as e:
|
|
st.error(f"Erreur lors du chargement de la fiche : {e}")
|