Code/utils/gitea.py
Stéphan Peccini f812fac89e
feat: Amelioration structure - tests, documentation et qualite du code
Cette mise a jour complete ameliore significativement la qualite et la maintenabilite du projet.

1. Extension de la couverture de tests

Couverture globale passee de 8% a 16% (+100%)
- Ajout de 25 nouveaux tests (total: 67 tests, 100% passent)
- Nouveaux fichiers de tests:
  * tests/unit/test_gitea.py (17 tests)
  * tests/unit/test_fiches_tickets.py (8 tests)

Etat de la couverture par module:
- utils/gitea.py: 100%
- utils/widgets.py: 100%
- utils/logger.py: 94%
- app/fiches/utils/tickets/core.py: 77%
- utils/graph_utils.py: 59%

2. Documentation d'architecture complete

Creation de 3 nouveaux documents (30 Ko total):
- docs/ARCHITECTURE.md (15 Ko)
  * Architecture complete du projet
  * Flux de donnees detailles
  * Indices de vulnerabilite (IHH, ISG, ICS, IVC)
  * Structure du graphe NetworkX

- docs/MODULES.md (15 Ko)
  * Guide des 11 modules principaux
  * Exemples de code (15+ snippets)
  * Bonnes pratiques
  * Guide de depannage

- docs/README.md (4 Ko)
  * Index de toute la documentation

Contenu documente:
- 5 modules applicatifs
- 6 modules utilitaires
- 4 indices de vulnerabilite avec formules et seuils
- Conventions de code

3. Reorganisation de la documentation

Structure finale optimisee:
- Racine: README.md (mis a jour) + Instructions.md
- docs/: 11 documents organises par categorie

Fichiers deplaces vers docs/:
- README_connexion.md -> docs/CONNEXION.md
- GUIDE_LOGS.md -> docs/
- GUIDE_RUFF.md -> docs/
- RAPPORT_RUFF.md -> docs/
- RAPPORT_CORRECTIONS_AUTO.md -> docs/
- REFACTORING_REPORT.md -> docs/
- VERIFICATION_LOGS.md -> docs/
- TODO_IA_BATCH.md -> docs/

4. Ajout de docstrings

52 fonctions documentees en style Google (100%)
Documentation en francais avec Args, Returns, Raises

5. Corrections automatiques Ruff

Application de 347 corrections automatiques:
- Formatage du code (line-length: 120)
- Organisation des imports
- Simplifications syntaxiques
- Suppressions de code mort
- Ameliorations de performance

6. Configuration qualite du code

Nouveaux fichiers:
- pyproject.toml: configuration Ruff complete
- .vscode/settings.json: integration Ruff avec formatOnSave
- GUIDE_RUFF.md: documentation du linter
- GUIDE_LOGS.md: documentation du logging
- .gitignore: ajout htmlcov/ pour rapports de couverture

Etat final du projet:
- Linter: Ruff configure (15 regles actives)
- Tests: 67 tests (100% passent)
- Couverture de code: 16%
- Docstrings: 52/52 (100%)
- Documentation: 11 fichiers organises

Impact:
- Tests plus robustes et maintenables
- Documentation technique complete
- Meilleure organisation des fichiers
- Workflow optimise avec Ruff
- Code pret pour integration continue

References:
- Architecture: docs/ARCHITECTURE.md
- Guide modules: docs/MODULES.md
- Tests: tests/unit/
- Configuration: pyproject.toml

Co-Authored-By: Claude <noreply@anthropic.com>
2026-02-07 19:00:49 +01:00

159 lines
6.2 KiB
Python

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)
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)
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)
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 {}