import base64 import logging from datetime import datetime, timezone from pathlib import Path 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 Path(nom_fichier).open(encoding="utf-8") as f: return f.read() 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 fichier = Path(nom_fichier) 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(fichier.stat().st_mtime, tz=timezone.utc) if fichier.exists() 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 fichier.open("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 Path(nom_fichier).exists(): 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() fichier = Path(fichier_local) 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(fichier.stat().st_mtime, tz=timezone.utc) if fichier.exists() 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 fichier.open("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 {}