import base64 import logging import os from datetime import datetime, timezone import requests import streamlit as st from dateutil import parser from config import DEPOT_CODE, DEPOT_FICHES, DOT_FILE, ENV, ENV_CODE, GITEA_TOKEN, GITEA_URL, ORGANISATION def lire_fichier_local(nom_fichier): """Lit le contenu d'un fichier local en UTF-8. Args: nom_fichier: Chemin vers le fichier a lire. Returns: str: Contenu du fichier. """ with open(nom_fichier, encoding="utf-8") as f: contenu_md = f.read() return contenu_md def charger_instructions_depuis_gitea(nom_fichier="Instructions.md"): """Charge le fichier Instructions.md depuis Gitea avec cache local timestamp. Telecharge le fichier depuis Gitea uniquement si la version distante est plus recente que la version locale. Utilise le cache local en priorite. Args: nom_fichier: Nom du fichier a charger. Defaut: "Instructions.md". Returns: str | None: Contenu du fichier en markdown, ou None si erreur sans cache. """ headers = {"Authorization": f"token {GITEA_TOKEN}"} url = f"{GITEA_URL}/repos/{ORGANISATION}/{DEPOT_FICHES}/contents/{nom_fichier}?ref={ENV}" try: # Vérifier si une version plus récente existe sur le dépôt remote_last_modified = recuperer_date_dernier_commit(f"{GITEA_URL}/repos/{ORGANISATION}/{DEPOT_FICHES}/commits?path={nom_fichier}&sha={ENV}") local_last_modified = datetime.fromtimestamp(os.path.getmtime(nom_fichier), tz=timezone.utc) if os.path.exists(nom_fichier) else None # Si le fichier local n'existe pas ou si la version distante est plus récente if not local_last_modified or (remote_last_modified and remote_last_modified > local_last_modified): response = requests.get(url, headers=headers, timeout=10) response.raise_for_status() data = response.json() contenu_md = base64.b64decode(data["content"]).decode("utf-8") # Sauvegarder en local with open(nom_fichier, "w", encoding="utf-8") as f: f.write(contenu_md) return contenu_md # Lire depuis le cache local return lire_fichier_local(nom_fichier) except Exception as e: st.error(f"Erreur chargement instructions Gitea : {e}") # Essayer de charger depuis le cache local en cas d'erreur if os.path.exists(nom_fichier): return lire_fichier_local(nom_fichier) return None def recuperer_date_dernier_commit(url): """Recupere la date du dernier commit pour un fichier via l'API Gitea. Args: url: URL de l'API Gitea pour les commits (format: /repos/.../commits?path=...&sha=...). Returns: datetime | None: Date du dernier commit en timezone-aware, ou None si erreur. """ headers = {"Authorization": f"token {GITEA_TOKEN}"} try: response = requests.get(url, headers=headers, timeout=10) response.raise_for_status() commits = response.json() if commits: return parser.isoparse(commits[0]["commit"]["author"]["date"]) except Exception as e: logging.error(f"Erreur récupération commit schema : {e}") return None def charger_schema_depuis_gitea(fichier_local="schema_temp.txt"): """Charge le schema DOT depuis Gitea avec cache local base sur timestamp. Telecharge le fichier schema DOT depuis le depot Gitea CODE uniquement si la version distante est plus recente que la version locale (ou si aucune version locale n'existe). Args: fichier_local: Chemin du fichier cache local. Defaut: "schema_temp.txt". Returns: str | None: "OK" si succes, None si erreur. """ headers = {"Authorization": f"token {GITEA_TOKEN}"} url = f"{GITEA_URL}/repos/{ORGANISATION}/{DEPOT_CODE}/contents/{DOT_FILE}?ref={ENV_CODE}" try: response = requests.get(url, headers=headers, timeout=10) response.raise_for_status() data = response.json() remote_last_modified = recuperer_date_dernier_commit(f"{GITEA_URL}/repos/{ORGANISATION}/{DEPOT_CODE}/commits?path={DOT_FILE}&sha={ENV_CODE}") local_last_modified = datetime.fromtimestamp(os.path.getmtime(fichier_local), tz=timezone.utc) if os.path.exists(fichier_local) else None if not local_last_modified or (remote_last_modified and remote_last_modified > local_last_modified): dot_text = base64.b64decode(data["content"]).decode("utf-8") with open(fichier_local, "w", encoding="utf-8") as f: f.write(dot_text) return "OK" except Exception as e: logging.error(f"Erreur chargement schema Gitea : {e}") return None def charger_arborescence_fiches(): """Charge l'arborescence complete des fiches depuis le depot Gitea. Recupere la liste des dossiers et fichiers .md dans le repertoire Documents du depot DEPOT_FICHES. Les resultats sont tries par ordre alphabetique. Returns: dict: Arborescence au format {nom_dossier: [{"nom": str, "download_url": str}, ...]}. Retourne un dict vide en cas d'erreur. """ headers = {"Authorization": f"token {GITEA_TOKEN}"} url_base = f"{GITEA_URL}/repos/{ORGANISATION}/{DEPOT_FICHES}/contents/Documents?ref={ENV}" try: response = requests.get(url_base, headers=headers, timeout=10) response.raise_for_status() dossiers = response.json() arbo = {} for dossier in sorted(dossiers, key=lambda d: d['name'].lower()): if dossier['type'] == 'dir': dossier_name = dossier['name'] url_dossier = dossier['url'] response_dossier = requests.get(url_dossier, headers=headers, timeout=10) response_dossier.raise_for_status() fichiers = response_dossier.json() fiches = sorted( [ {"nom": f["name"], "download_url": f["download_url"]} for f in fichiers if f["name"].endswith(".md") ], key=lambda x: x['nom'].lower() ) arbo[dossier_name] = fiches return arbo except Exception as e: logging.error(f"Erreur chargement fiches : {e}") return {}