gtyd
Browse files
app.py
CHANGED
|
@@ -11,41 +11,39 @@ import fitz
|
|
| 11 |
import json
|
| 12 |
from pathlib import Path
|
| 13 |
from tqdm import tqdm
|
| 14 |
-
|
|
|
|
| 15 |
import requests
|
| 16 |
import zipfile
|
| 17 |
import io
|
| 18 |
import os
|
| 19 |
-
from huggingface_hub import hf_hub_download, HfApi
|
| 20 |
from datetime import datetime
|
| 21 |
-
import uuid
|
| 22 |
|
| 23 |
# ---------- CONFIG ----------
|
| 24 |
-
MODEL = "mistralai/Mistral-7B-Instruct-v0.3"
|
| 25 |
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
ZIP_FILENAME_IN_DATASET = "brvm_reports.zip" # Le nom du fichier ZIP à l'intérieur de ce dataset
|
| 29 |
|
| 30 |
-
# Hugging Face Dataset où uploader les résultats JSON
|
| 31 |
HF_DATASET_REPO_ID_JSON_OUTPUT = "lamekemal/brvm-reports-json"
|
| 32 |
|
| 33 |
-
# Vous devez ajouter votre jeton Hugging Face (avec rôle "Write" pour l'upload)
|
| 34 |
-
# comme un secret dans les paramètres de votre Hugging Face Space.
|
| 35 |
-
# Le nom du secret DOIT être HF_TOKEN.
|
| 36 |
-
# os.getenv('HF_TOKEN') récupérera automatiquement cette valeur.
|
| 37 |
HF_TOKEN = os.getenv('HF_TOKEN')
|
| 38 |
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
LOCAL_OUT_BASE_FOLDER = "local_json_outputs" # Dossier de base local pour les sorties JSON temporaires
|
| 42 |
# ----------------------------
|
| 43 |
|
|
|
|
| 44 |
PROMPT_TEMPLATE = """
|
| 45 |
-
Tu es un expert en finance spécialisé dans la BRVM.
|
| 46 |
À partir du texte ci-dessous issu d’un bulletin officiel de la cote BRVM,
|
| 47 |
-
|
|
|
|
|
|
|
| 48 |
|
|
|
|
| 49 |
{{
|
| 50 |
"indicateurs": {{
|
| 51 |
"brvm_10": {{ "niveau": float, "var_jour_pct": float, "var_annuelle_pct": float }},
|
|
@@ -73,27 +71,49 @@ extrait uniquement les données suivantes et retourne un JSON valide au format s
|
|
| 73 |
}}
|
| 74 |
|
| 75 |
Contraintes :
|
| 76 |
-
-
|
| 77 |
-
-
|
| 78 |
-
- Les nombres utilisent un point comme séparateur décimal
|
| 79 |
|
| 80 |
Texte du bulletin :
|
| 81 |
-
{texte_pdf}
|
| 82 |
"""
|
|
|
|
|
|
|
| 83 |
|
| 84 |
-
# --- Initialisation du pipeline Hugging Face ---
|
| 85 |
try:
|
| 86 |
-
print("Chargement du modèle Hugging Face...")
|
| 87 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 88 |
extractor = pipeline(
|
| 89 |
"text-generation",
|
| 90 |
-
model=
|
|
|
|
| 91 |
device=0 # Utilise le GPU si disponible (0 pour le premier GPU, -1 pour CPU)
|
| 92 |
)
|
| 93 |
-
print("Modèle chargé avec succès.")
|
| 94 |
except Exception as e:
|
| 95 |
print(f"Erreur lors du chargement du modèle Hugging Face : {e}")
|
| 96 |
-
extractor
|
| 97 |
|
| 98 |
# --- Fonctions d'extraction et de traitement ---
|
| 99 |
def extract_text(pdf_path):
|
|
@@ -109,32 +129,63 @@ def extract_text(pdf_path):
|
|
| 109 |
def call_huggingface_model(text):
|
| 110 |
"""Appel du modèle Hugging Face pour extraire le JSON à partir du texte."""
|
| 111 |
if extractor is None:
|
|
|
|
| 112 |
return {"error": "Modèle non chargé."}
|
| 113 |
|
| 114 |
prompt = PROMPT_TEMPLATE.format(texte_pdf=text)
|
| 115 |
|
| 116 |
try:
|
| 117 |
-
#
|
| 118 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 119 |
|
| 120 |
-
# Le résultat est souvent une liste de dictionnaires, on prend le premier
|
| 121 |
output_text = response[0]['generated_text']
|
| 122 |
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 126 |
|
| 127 |
if json_start != -1 and json_end != -1:
|
| 128 |
-
|
| 129 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 130 |
else:
|
| 131 |
-
return {"error": "JSON non
|
| 132 |
|
| 133 |
except json.JSONDecodeError:
|
| 134 |
-
print(f"Erreur JSONDecodeError:
|
| 135 |
-
return {"error": "JSON invalide", "raw": output_text}
|
| 136 |
except Exception as e:
|
| 137 |
-
return {"error": str(e)}
|
| 138 |
|
| 139 |
def download_and_extract_zip_from_hf_dataset(dataset_repo_id, zip_filename, target_folder):
|
| 140 |
"""
|
|
@@ -144,34 +195,28 @@ def download_and_extract_zip_from_hf_dataset(dataset_repo_id, zip_filename, targ
|
|
| 144 |
print(f"Tentative de téléchargement du fichier '{zip_filename}' "
|
| 145 |
f"depuis le dataset Hugging Face : {dataset_repo_id}")
|
| 146 |
try:
|
| 147 |
-
# Télécharger le fichier ZIP depuis le dataset Hugging Face Hub
|
| 148 |
local_zip_path = hf_hub_download(
|
| 149 |
repo_id=dataset_repo_id,
|
| 150 |
filename=zip_filename,
|
| 151 |
repo_type="dataset",
|
| 152 |
-
cache_dir="./hf_cache"
|
| 153 |
)
|
| 154 |
print(f"Fichier ZIP téléchargé localement : {local_zip_path}")
|
| 155 |
|
| 156 |
-
# Créer le dossier cible s'il n'existe pas
|
| 157 |
Path(target_folder).mkdir(parents=True, exist_ok=True)
|
| 158 |
|
| 159 |
extracted_files = []
|
| 160 |
with zipfile.ZipFile(local_zip_path, 'r') as z:
|
| 161 |
for file_info in z.infolist():
|
| 162 |
-
# Construire le chemin complet du fichier extrait
|
| 163 |
-
# Éviter les chemins absolus ou les traversées de répertoire pour la sécurité
|
| 164 |
if file_info.filename.startswith('/') or '..' in file_info.filename:
|
| 165 |
print(f"Skipping potentially unsafe path: {file_info.filename}")
|
| 166 |
continue
|
| 167 |
|
| 168 |
extracted_path = Path(target_folder) / file_info.filename
|
| 169 |
|
| 170 |
-
# Créer les sous-dossiers si nécessaire
|
| 171 |
if file_info.is_dir():
|
| 172 |
extracted_path.mkdir(parents=True, exist_ok=True)
|
| 173 |
elif file_info.filename.lower().endswith('.pdf'):
|
| 174 |
-
# Extraire uniquement les fichiers PDF
|
| 175 |
print(f"Extraction de : {file_info.filename} vers {extracted_path}")
|
| 176 |
try:
|
| 177 |
with extracted_path.open("wb") as outfile:
|
|
@@ -189,13 +234,11 @@ def download_and_extract_zip_from_hf_dataset(dataset_repo_id, zip_filename, targ
|
|
| 189 |
print(f"Une erreur est survenue lors du téléchargement ou de l'extraction depuis Hugging Face Dataset : {e}")
|
| 190 |
return []
|
| 191 |
finally:
|
| 192 |
-
# Laisser hf_hub_download gérer son propre cache.
|
| 193 |
pass
|
| 194 |
|
| 195 |
# --- Fonction principale ---
|
| 196 |
def main():
|
| 197 |
"""Fonction principale pour exécuter l'ensemble du processus d'extraction."""
|
| 198 |
-
# 1. Télécharger et extraire les PDF depuis le Hugging Face Dataset
|
| 199 |
downloaded_pdfs = download_and_extract_zip_from_hf_dataset(
|
| 200 |
HF_DATASET_REPO_ID_PDFS, ZIP_FILENAME_IN_DATASET, PDF_FOLDER
|
| 201 |
)
|
|
@@ -204,8 +247,6 @@ def main():
|
|
| 204 |
print("Aucun fichier PDF n'a pu être téléchargé ou extrait. Arrêt du processus.")
|
| 205 |
return
|
| 206 |
|
| 207 |
-
# 2. Préparer le dossier de sortie LOCAL unique pour les JSON
|
| 208 |
-
# Créer un nom de dossier unique basé sur la date/heure et un UUID
|
| 209 |
unique_folder_name = datetime.now().strftime("%Y-%m-%d_%H-%M-%S") + "_" + str(uuid.uuid4())[:8]
|
| 210 |
local_out_dir = Path(LOCAL_OUT_BASE_FOLDER) / unique_folder_name
|
| 211 |
local_out_dir.mkdir(parents=True, exist_ok=True)
|
|
@@ -223,31 +264,25 @@ def main():
|
|
| 223 |
data = call_huggingface_model(text)
|
| 224 |
aggregate.append(data)
|
| 225 |
|
| 226 |
-
# Sauvegarde par fichier dans le dossier local unique
|
| 227 |
out_path = local_out_dir / f"{pdf_file.stem}.json"
|
| 228 |
with open(out_path, "w", encoding="utf-8") as f:
|
| 229 |
json.dump(data, f, ensure_ascii=False, indent=2)
|
| 230 |
|
| 231 |
-
# Sauvegarde du fichier global aggrégé dans le dossier local unique
|
| 232 |
with open(local_out_dir / "brvm_aggregate.json", "w", encoding="utf-8") as f:
|
| 233 |
json.dump(aggregate, f, ensure_ascii=False, indent=2)
|
| 234 |
|
| 235 |
print(f"[OK] Extraction terminée - {len(downloaded_pdfs)} fichiers traités dans {local_out_dir}.")
|
| 236 |
|
| 237 |
-
# 3. Uploader les résultats vers le Hugging Face Dataset dédié
|
| 238 |
if HF_TOKEN:
|
| 239 |
try:
|
| 240 |
api = HfApi(token=HF_TOKEN)
|
| 241 |
print(f"Début de l'upload des résultats vers {HF_DATASET_REPO_ID_JSON_OUTPUT}/{unique_folder_name}...")
|
| 242 |
|
| 243 |
-
|
| 244 |
-
api.upload_folder( # Note: upload_folder est recommandé même pour les "large folders" pour les datasets
|
| 245 |
-
# C'est le message d'avertissement qui est trompeur.
|
| 246 |
-
# L'API gère LFS en interne pour les gros fichiers.
|
| 247 |
folder_path=str(local_out_dir),
|
| 248 |
repo_id=HF_DATASET_REPO_ID_JSON_OUTPUT,
|
| 249 |
repo_type="dataset",
|
| 250 |
-
path_in_repo=unique_folder_name,
|
| 251 |
commit_message=f"Extraction BRVM du {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
|
| 252 |
)
|
| 253 |
print(f"[OK] Upload terminé vers https://huggingface.co/datasets/{HF_DATASET_REPO_ID_JSON_OUTPUT}/tree/main/{unique_folder_name}")
|
|
@@ -260,6 +295,5 @@ def main():
|
|
| 260 |
print(f"Les fichiers sont disponibles localement dans le Space à l'adresse : {local_out_dir}")
|
| 261 |
|
| 262 |
|
| 263 |
-
# Point d'entrée du script
|
| 264 |
if __name__ == "__main__":
|
| 265 |
main()
|
|
|
|
| 11 |
import json
|
| 12 |
from pathlib import Path
|
| 13 |
from tqdm import tqdm
|
| 14 |
+
import torch
|
| 15 |
+
from transformers import pipeline, AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig
|
| 16 |
import requests
|
| 17 |
import zipfile
|
| 18 |
import io
|
| 19 |
import os
|
| 20 |
+
from huggingface_hub import hf_hub_download, HfApi
|
| 21 |
from datetime import datetime
|
| 22 |
+
import uuid
|
| 23 |
|
| 24 |
# ---------- CONFIG ----------
|
| 25 |
+
MODEL = "mistralai/Mistral-7B-Instruct-v0.3"
|
| 26 |
|
| 27 |
+
HF_DATASET_REPO_ID_PDFS = "lamekemal/brvm-reports-pdfs"
|
| 28 |
+
ZIP_FILENAME_IN_DATASET = "brvm_reports.zip"
|
|
|
|
| 29 |
|
|
|
|
| 30 |
HF_DATASET_REPO_ID_JSON_OUTPUT = "lamekemal/brvm-reports-json"
|
| 31 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 32 |
HF_TOKEN = os.getenv('HF_TOKEN')
|
| 33 |
|
| 34 |
+
PDF_FOLDER = "brvm_reports"
|
| 35 |
+
LOCAL_OUT_BASE_FOLDER = "local_json_outputs"
|
|
|
|
| 36 |
# ----------------------------
|
| 37 |
|
| 38 |
+
# Prompt optimisé pour le modèle Mistral Instruct
|
| 39 |
PROMPT_TEMPLATE = """
|
| 40 |
+
[INST] Tu es un expert en finance spécialisé dans la BRVM.
|
| 41 |
À partir du texte ci-dessous issu d’un bulletin officiel de la cote BRVM,
|
| 42 |
+
EXTRAIT UNIQUEMENT les données suivantes et RETOURNE UN JSON VALIDE au format strict.
|
| 43 |
+
Ta réponse NE DOIT CONTENIR AUCUN AUTRE TEXTE QUE LE JSON.
|
| 44 |
+
Commence ta réponse par [DEBUT_JSON] et termine la par [FIN_JSON].
|
| 45 |
|
| 46 |
+
JSON Schema:
|
| 47 |
{{
|
| 48 |
"indicateurs": {{
|
| 49 |
"brvm_10": {{ "niveau": float, "var_jour_pct": float, "var_annuelle_pct": float }},
|
|
|
|
| 71 |
}}
|
| 72 |
|
| 73 |
Contraintes :
|
| 74 |
+
- Si une donnée est absente, mets null.
|
| 75 |
+
- Les nombres utilisent un point comme séparateur décimal.
|
|
|
|
| 76 |
|
| 77 |
Texte du bulletin :
|
| 78 |
+
{texte_pdf}[/INST]
|
| 79 |
"""
|
| 80 |
+
# --- Initialisation du pipeline Hugging Face avec quantification ---
|
| 81 |
+
extractor = None # Initialiser à None pour gérer les erreurs de chargement
|
| 82 |
|
|
|
|
| 83 |
try:
|
| 84 |
+
print("Chargement du modèle Hugging Face avec quantification 4-bit...")
|
| 85 |
+
|
| 86 |
+
# Configuration de la quantification 4-bit
|
| 87 |
+
bnb_config = BitsAndBytesConfig(
|
| 88 |
+
load_in_4bit=True,
|
| 89 |
+
bnb_4bit_quant_type="nf4", # NormalFloat4 quantization
|
| 90 |
+
bnb_4bit_compute_dtype=torch.bfloat16, # Type de données pour les calculs
|
| 91 |
+
bnb_4bit_use_double_quant=True, # Double quantification pour plus de précision
|
| 92 |
+
)
|
| 93 |
+
|
| 94 |
+
# Charger le tokenizer et le modèle avec la configuration de quantification
|
| 95 |
+
tokenizer = AutoTokenizer.from_pretrained(MODEL)
|
| 96 |
+
model = AutoModelForCausalLM.from_pretrained(
|
| 97 |
+
MODEL,
|
| 98 |
+
quantization_config=bnb_config,
|
| 99 |
+
device_map="auto", # Permet de charger le modèle sur le(s) GPU disponible(s)
|
| 100 |
+
torch_dtype=torch.bfloat16 # Spécifier le dtype pour le chargement initial
|
| 101 |
+
)
|
| 102 |
+
|
| 103 |
+
# Créer le pipeline avec le modèle et le tokenizer chargés
|
| 104 |
+
if tokenizer.pad_token is None:
|
| 105 |
+
tokenizer.pad_token = tokenizer.eos_token # Ou un autre token approprié si nécessaire
|
| 106 |
+
|
| 107 |
extractor = pipeline(
|
| 108 |
"text-generation",
|
| 109 |
+
model=model,
|
| 110 |
+
tokenizer=tokenizer,
|
| 111 |
device=0 # Utilise le GPU si disponible (0 pour le premier GPU, -1 pour CPU)
|
| 112 |
)
|
| 113 |
+
print("Modèle quantifié chargé avec succès.")
|
| 114 |
except Exception as e:
|
| 115 |
print(f"Erreur lors du chargement du modèle Hugging Face : {e}")
|
| 116 |
+
# Ne pas initialiser extractor si erreur
|
| 117 |
|
| 118 |
# --- Fonctions d'extraction et de traitement ---
|
| 119 |
def extract_text(pdf_path):
|
|
|
|
| 129 |
def call_huggingface_model(text):
|
| 130 |
"""Appel du modèle Hugging Face pour extraire le JSON à partir du texte."""
|
| 131 |
if extractor is None:
|
| 132 |
+
print("[Erreur] Le pipeline d'extraction n'a pas été initialisé. Impossible d'appeler le modèle.")
|
| 133 |
return {"error": "Modèle non chargé."}
|
| 134 |
|
| 135 |
prompt = PROMPT_TEMPLATE.format(texte_pdf=text)
|
| 136 |
|
| 137 |
try:
|
| 138 |
+
# Augmenter considérablement max_new_tokens pour éviter la troncature
|
| 139 |
+
# Augmenté de 4096 à 8192
|
| 140 |
+
response = extractor(
|
| 141 |
+
prompt,
|
| 142 |
+
max_new_tokens=8192,
|
| 143 |
+
do_sample=False,
|
| 144 |
+
num_return_sequences=1
|
| 145 |
+
)
|
| 146 |
|
|
|
|
| 147 |
output_text = response[0]['generated_text']
|
| 148 |
|
| 149 |
+
json_start_marker = "[DEBUT_JSON]"
|
| 150 |
+
json_end_marker = "[FIN_JSON]"
|
| 151 |
+
|
| 152 |
+
# La réponse du modèle Mistral peut inclure le prompt, donc on cherche le début du JSON APRÈS la fin du prompt ou le début du marqueur
|
| 153 |
+
# et la fin du JSON AVANT la fin du marqueur.
|
| 154 |
+
|
| 155 |
+
# Trouver la fin du prompt pour commencer la recherche du JSON
|
| 156 |
+
prompt_end_index = output_text.rfind("[/INST]") # C'est le marqueur de fin d'instruction
|
| 157 |
+
|
| 158 |
+
# Si le marqueur [/INST] est trouvé, commencer la recherche du JSON après.
|
| 159 |
+
if prompt_end_index != -1:
|
| 160 |
+
search_start_index = prompt_end_index + len("[/INST]")
|
| 161 |
+
text_to_parse = output_text[search_start_index:]
|
| 162 |
+
else:
|
| 163 |
+
# Si le marqueur [/INST] n'est pas trouvé (moins probable avec un modèle instruct),
|
| 164 |
+
# on prend toute la chaîne et on se base sur les marqueurs [DEBUT_JSON]/[FIN_JSON]
|
| 165 |
+
text_to_parse = output_text
|
| 166 |
+
|
| 167 |
+
json_start = text_to_parse.find(json_start_marker)
|
| 168 |
+
json_end = text_to_parse.rfind(json_end_marker)
|
| 169 |
|
| 170 |
if json_start != -1 and json_end != -1:
|
| 171 |
+
json_text_with_markers = text_to_parse[json_start + len(json_start_marker):json_end]
|
| 172 |
+
|
| 173 |
+
actual_json_start = json_text_with_markers.find('{')
|
| 174 |
+
actual_json_end = json_text_with_markers.rfind('}') + 1
|
| 175 |
+
|
| 176 |
+
if actual_json_start != -1 and actual_json_end != -1:
|
| 177 |
+
json_content = json_text_with_markers[actual_json_start:actual_json_end]
|
| 178 |
+
return json.loads(json_content)
|
| 179 |
+
else:
|
| 180 |
+
return {"error": "JSON non trouvé entre les accolades après les marqueurs", "raw": output_text}
|
| 181 |
else:
|
| 182 |
+
return {"error": "Marqueurs JSON non trouvés", "raw": output_text}
|
| 183 |
|
| 184 |
except json.JSONDecodeError:
|
| 185 |
+
print(f"Erreur JSONDecodeError: Le texte brut était : {output_text[:500]}...")
|
| 186 |
+
return {"error": "JSON invalide ou mal formé", "raw": output_text}
|
| 187 |
except Exception as e:
|
| 188 |
+
return {"error": str(e), "raw": output_text}
|
| 189 |
|
| 190 |
def download_and_extract_zip_from_hf_dataset(dataset_repo_id, zip_filename, target_folder):
|
| 191 |
"""
|
|
|
|
| 195 |
print(f"Tentative de téléchargement du fichier '{zip_filename}' "
|
| 196 |
f"depuis le dataset Hugging Face : {dataset_repo_id}")
|
| 197 |
try:
|
|
|
|
| 198 |
local_zip_path = hf_hub_download(
|
| 199 |
repo_id=dataset_repo_id,
|
| 200 |
filename=zip_filename,
|
| 201 |
repo_type="dataset",
|
| 202 |
+
cache_dir="./hf_cache"
|
| 203 |
)
|
| 204 |
print(f"Fichier ZIP téléchargé localement : {local_zip_path}")
|
| 205 |
|
|
|
|
| 206 |
Path(target_folder).mkdir(parents=True, exist_ok=True)
|
| 207 |
|
| 208 |
extracted_files = []
|
| 209 |
with zipfile.ZipFile(local_zip_path, 'r') as z:
|
| 210 |
for file_info in z.infolist():
|
|
|
|
|
|
|
| 211 |
if file_info.filename.startswith('/') or '..' in file_info.filename:
|
| 212 |
print(f"Skipping potentially unsafe path: {file_info.filename}")
|
| 213 |
continue
|
| 214 |
|
| 215 |
extracted_path = Path(target_folder) / file_info.filename
|
| 216 |
|
|
|
|
| 217 |
if file_info.is_dir():
|
| 218 |
extracted_path.mkdir(parents=True, exist_ok=True)
|
| 219 |
elif file_info.filename.lower().endswith('.pdf'):
|
|
|
|
| 220 |
print(f"Extraction de : {file_info.filename} vers {extracted_path}")
|
| 221 |
try:
|
| 222 |
with extracted_path.open("wb") as outfile:
|
|
|
|
| 234 |
print(f"Une erreur est survenue lors du téléchargement ou de l'extraction depuis Hugging Face Dataset : {e}")
|
| 235 |
return []
|
| 236 |
finally:
|
|
|
|
| 237 |
pass
|
| 238 |
|
| 239 |
# --- Fonction principale ---
|
| 240 |
def main():
|
| 241 |
"""Fonction principale pour exécuter l'ensemble du processus d'extraction."""
|
|
|
|
| 242 |
downloaded_pdfs = download_and_extract_zip_from_hf_dataset(
|
| 243 |
HF_DATASET_REPO_ID_PDFS, ZIP_FILENAME_IN_DATASET, PDF_FOLDER
|
| 244 |
)
|
|
|
|
| 247 |
print("Aucun fichier PDF n'a pu être téléchargé ou extrait. Arrêt du processus.")
|
| 248 |
return
|
| 249 |
|
|
|
|
|
|
|
| 250 |
unique_folder_name = datetime.now().strftime("%Y-%m-%d_%H-%M-%S") + "_" + str(uuid.uuid4())[:8]
|
| 251 |
local_out_dir = Path(LOCAL_OUT_BASE_FOLDER) / unique_folder_name
|
| 252 |
local_out_dir.mkdir(parents=True, exist_ok=True)
|
|
|
|
| 264 |
data = call_huggingface_model(text)
|
| 265 |
aggregate.append(data)
|
| 266 |
|
|
|
|
| 267 |
out_path = local_out_dir / f"{pdf_file.stem}.json"
|
| 268 |
with open(out_path, "w", encoding="utf-8") as f:
|
| 269 |
json.dump(data, f, ensure_ascii=False, indent=2)
|
| 270 |
|
|
|
|
| 271 |
with open(local_out_dir / "brvm_aggregate.json", "w", encoding="utf-8") as f:
|
| 272 |
json.dump(aggregate, f, ensure_ascii=False, indent=2)
|
| 273 |
|
| 274 |
print(f"[OK] Extraction terminée - {len(downloaded_pdfs)} fichiers traités dans {local_out_dir}.")
|
| 275 |
|
|
|
|
| 276 |
if HF_TOKEN:
|
| 277 |
try:
|
| 278 |
api = HfApi(token=HF_TOKEN)
|
| 279 |
print(f"Début de l'upload des résultats vers {HF_DATASET_REPO_ID_JSON_OUTPUT}/{unique_folder_name}...")
|
| 280 |
|
| 281 |
+
api.upload_folder(
|
|
|
|
|
|
|
|
|
|
| 282 |
folder_path=str(local_out_dir),
|
| 283 |
repo_id=HF_DATASET_REPO_ID_JSON_OUTPUT,
|
| 284 |
repo_type="dataset",
|
| 285 |
+
path_in_repo=unique_folder_name,
|
| 286 |
commit_message=f"Extraction BRVM du {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
|
| 287 |
)
|
| 288 |
print(f"[OK] Upload terminé vers https://huggingface.co/datasets/{HF_DATASET_REPO_ID_JSON_OUTPUT}/tree/main/{unique_folder_name}")
|
|
|
|
| 295 |
print(f"Les fichiers sont disponibles localement dans le Space à l'adresse : {local_out_dir}")
|
| 296 |
|
| 297 |
|
|
|
|
| 298 |
if __name__ == "__main__":
|
| 299 |
main()
|