lamekemal commited on
Commit
b0efaca
·
1 Parent(s): 2fabfe9
Files changed (2) hide show
  1. app.py +75 -64
  2. requirements.txt +1 -1
app.py CHANGED
@@ -2,7 +2,7 @@
2
  # -*- coding: utf-8 -*-
3
  """
4
  Extraction BRVM via Hugging Face Hub sur Space
5
- Avec téléchargement et extraction de PDF depuis Google Drive ZIP
6
  Auteur : Gemini
7
  """
8
 
@@ -13,16 +13,16 @@ from tqdm import tqdm
13
  from transformers import pipeline
14
  import requests
15
  import zipfile
16
- import io # Pour gérer le contenu du fichier ZIP en mémoire
17
-
 
18
 
19
  # ---------- CONFIG ----------
20
- MODEL = "mistralai/Mistral-7B-Instruct-v0.2" # Exemple de modèle open source
21
- # URL DIRECTE du fichier ZIP sur Google Drive
22
- # Assurez-vous que ce lien est PUBLIC et qu'il est au format de téléchargement direct.
23
- # Exemple: si votre lien partagé est https://drive.google.com/file/d/VOTRE_FILE_ID/view?usp=sharing
24
- # Le lien direct sera: https://drive.google.com/uc?export=download&id=1eTg1YGGYeoMxwo3sorhYFpZGdNVmrsnX
25
- GOOGLE_DRIVE_ZIP_URL = "https://drive.usercontent.google.com/download?id=1eTg1YGGYeoMxwo3sorhYFpZGdNVmrsnX&export=download&authuser=0&confirm=t&uuid=158a5f62-9495-4f6f-904d-7f9a1ae35d53&at=AN8xHoof0ooMa_xauqBwiH0JNDPC%3A1754958945113"
26
 
27
  PDF_FOLDER = "brvm_reports" # Dossier où les PDF seront extraits et traités
28
  OUT_FOLDER = "out_brvm_hf" # Dossier où les fichiers de sortie JSON seront sauvegardés
@@ -33,31 +33,31 @@ Tu es un expert en finance spécialisé dans la BRVM.
33
  À partir du texte ci-dessous issu d’un bulletin officiel de la cote BRVM,
34
  extrait uniquement les données suivantes et retourne un JSON valide au format strict :
35
 
36
- {
37
- "indicateurs": {
38
- "brvm_10": { "niveau": float, "var_jour_pct": float, "var_annuelle_pct": float },
39
- "brvm_composite": { "niveau": float, "var_jour_pct": float, "var_annuelle_pct": float },
40
  "capitalisation_actions_fcfa": float,
41
  "capitalisation_obligations_fcfa": float,
42
  "volume_echange": float,
43
  "valeur_transigee_fcfa": float
44
- },
45
  "plus_fortes_hausses": [
46
- { "symbol": string, "nom": string, "cours": float, "var_jour_pct": float, "var_annuelle_pct": float }
47
  ],
48
  "plus_fortes_baisses": [
49
- { "symbol": string, "nom": string, "cours": float, "var_jour_pct": float, "var_annuelle_pct": float }
50
  ],
51
  "actions": [
52
- { "symbol": string, "nom": string, "cours_jour": float, "var_jour_pct": float, "volume": float, "valeur_fcfa": float, "dernier_dividende": float|null, "date_dividende": string|null }
53
  ],
54
  "dividendes": [
55
- { "symbol": string, "nom": string, "montant_fcfa": float, "date_paiement": string }
56
  ],
57
  "obligations": [
58
- { "code": string, "emetteur": string, "coupon_pct": float, "echeance": string, "nominal": float, "cours": float }
59
  ]
60
- }
61
 
62
  Contraintes :
63
  - Ne mets aucun texte hors du JSON
@@ -67,14 +67,15 @@ Contraintes :
67
  Texte du bulletin :
68
  {texte_pdf}
69
  """
 
70
  # --- Initialisation du pipeline Hugging Face ---
71
  try:
72
  print("Chargement du modèle Hugging Face...")
73
- # On spécifie le device=0 pour utiliser le premier GPU disponible
74
  extractor = pipeline(
75
  "text-generation",
76
  model=MODEL,
77
- device=0 # Utilise le GPU si disponible
78
  )
79
  print("Modèle chargé avec succès.")
80
  except Exception as e:
@@ -117,69 +118,79 @@ def call_huggingface_model(text):
117
  return {"error": "JSON non trouvé dans la réponse", "raw": output_text}
118
 
119
  except json.JSONDecodeError:
 
120
  return {"error": "JSON invalide", "raw": output_text}
121
  except Exception as e:
122
  return {"error": str(e)}
123
 
124
- def download_and_extract_zip(zip_url, target_folder):
125
  """
126
- Télécharge un fichier ZIP depuis une URL et extrait les PDF dans un dossier cible.
127
- Crée le dossier cible s'il n'existe pas.
128
  """
129
- print(f"Tentative de téléchargement du fichier ZIP depuis : {zip_url}")
 
130
  try:
131
- response = requests.get(zip_url, stream=True)
132
- response.raise_for_status() # Lève une exception pour les codes d'état HTTP d'erreur
133
-
 
 
 
 
 
 
134
  # Créer le dossier cible s'il n'existe pas
135
  Path(target_folder).mkdir(parents=True, exist_ok=True)
136
 
137
- # Lire le contenu du ZIP en mémoire
138
- z = zipfile.ZipFile(io.BytesIO(response.content))
139
-
140
  extracted_files = []
141
- for file_info in z.infolist():
142
- # Construire le chemin complet du fichier extrait
143
- # Éviter les chemins absolus ou les traversées de répertoire pour la sécurité
144
- if file_info.filename.startswith('/') or '..' in file_info.filename:
145
- print(f"Skipping potentially unsafe path: {file_info.filename}")
146
- continue
147
-
148
- extracted_path = Path(target_folder) / file_info.filename
149
-
150
- # Créer les sous-dossiers si nécessaire
151
- if file_info.is_dir():
152
- extracted_path.mkdir(parents=True, exist_ok=True)
153
- elif file_info.filename.lower().endswith('.pdf'):
154
- # Extraire uniquement les fichiers PDF
155
- print(f"Extraction de : {file_info.filename} vers {extracted_path}")
156
- try:
157
- with extracted_path.open("wb") as outfile:
158
- outfile.write(z.read(file_info.filename))
159
- extracted_files.append(extracted_path)
160
- except Exception as e:
161
- print(f"Erreur lors de l'extraction de {file_info.filename}: {e}")
162
- else:
163
- print(f"Ignoré (non PDF) : {file_info.filename}")
 
164
 
165
  print(f"Extraction terminée. {len(extracted_files)} fichiers PDF extraits.")
166
  return extracted_files
167
 
168
- except requests.exceptions.RequestException as e:
169
- print(f"Erreur lors du téléchargement du ZIP : {e}")
170
- return []
171
- except zipfile.BadZipFile:
172
- print("Erreur : Le fichier téléchargé n'est pas un fichier ZIP valide.")
173
- return []
174
  except Exception as e:
175
- print(f"Une erreur inattendue est survenue lors de l'extraction : {e}")
176
  return []
 
 
 
 
 
 
 
 
177
 
178
  # --- Fonction principale ---
179
  def main():
180
  """Fonction principale pour exécuter l'ensemble du processus d'extraction."""
181
- # 1. Télécharger et extraire les PDF
182
- downloaded_pdfs = download_and_extract_zip(GOOGLE_DRIVE_ZIP_URL, PDF_FOLDER)
 
 
183
 
184
  if not downloaded_pdfs:
185
  print("Aucun fichier PDF n'a pu être téléchargé ou extrait. Arrêt du processus.")
 
2
  # -*- coding: utf-8 -*-
3
  """
4
  Extraction BRVM via Hugging Face Hub sur Space
5
+ Avec téléchargement et extraction de PDF depuis un Hugging Face Dataset
6
  Auteur : Gemini
7
  """
8
 
 
13
  from transformers import pipeline
14
  import requests
15
  import zipfile
16
+ import io
17
+ import os
18
+ from huggingface_hub import hf_hub_download # Importation pour télécharger depuis HF Hub
19
 
20
  # ---------- CONFIG ----------
21
+ MODEL = "mistralai/Mistral-7B-Instruct-v0.2" # Modèle Hugging Face à utiliser
22
+
23
+ # Hugging Face Dataset d' télécharger le fichier ZIP
24
+ HF_DATASET_REPO_ID = "lamekemal/brvm-reports-pdfs" # L'ID de votre dépôt de dataset
25
+ ZIP_FILENAME_IN_DATASET = "brvm_reports.zip" # Le nom du fichier ZIP à l'intérieur de ce dataset
 
26
 
27
  PDF_FOLDER = "brvm_reports" # Dossier où les PDF seront extraits et traités
28
  OUT_FOLDER = "out_brvm_hf" # Dossier où les fichiers de sortie JSON seront sauvegardés
 
33
  À partir du texte ci-dessous issu d’un bulletin officiel de la cote BRVM,
34
  extrait uniquement les données suivantes et retourne un JSON valide au format strict :
35
 
36
+ {{
37
+ "indicateurs": {{
38
+ "brvm_10": {{ "niveau": float, "var_jour_pct": float, "var_annuelle_pct": float }},
39
+ "brvm_composite": {{ "niveau": float, "var_jour_pct": float, "var_annuelle_pct": float }},
40
  "capitalisation_actions_fcfa": float,
41
  "capitalisation_obligations_fcfa": float,
42
  "volume_echange": float,
43
  "valeur_transigee_fcfa": float
44
+ }},
45
  "plus_fortes_hausses": [
46
+ {{ "symbol": string, "nom": string, "cours": float, "var_jour_pct": float, "var_annuelle_pct": float }}
47
  ],
48
  "plus_fortes_baisses": [
49
+ {{ "symbol": string, "nom": string, "cours": float, "var_jour_pct": float, "var_annuelle_pct": float }}
50
  ],
51
  "actions": [
52
+ {{ "symbol": string, "nom": string, "cours_jour": float, "var_jour_pct": float, "volume": float, "valeur_fcfa": float, "dernier_dividende": float|null, "date_dividende": string|null }}
53
  ],
54
  "dividendes": [
55
+ {{ "symbol": string, "nom": string, "montant_fcfa": float, "date_paiement": string }}
56
  ],
57
  "obligations": [
58
+ {{ "code": string, "emetteur": string, "coupon_pct": float, "echeance": string, "nominal": float, "cours": float }}
59
  ]
60
+ }}
61
 
62
  Contraintes :
63
  - Ne mets aucun texte hors du JSON
 
67
  Texte du bulletin :
68
  {texte_pdf}
69
  """
70
+
71
  # --- Initialisation du pipeline Hugging Face ---
72
  try:
73
  print("Chargement du modèle Hugging Face...")
74
+ # Le pipeline utilisera automatiquement le jeton HF_TOKEN si disponible en tant que secret/variable d'environnement
75
  extractor = pipeline(
76
  "text-generation",
77
  model=MODEL,
78
+ device=0 # Utilise le GPU si disponible (0 pour le premier GPU, -1 pour CPU)
79
  )
80
  print("Modèle chargé avec succès.")
81
  except Exception as e:
 
118
  return {"error": "JSON non trouvé dans la réponse", "raw": output_text}
119
 
120
  except json.JSONDecodeError:
121
+ print(f"Erreur JSONDecodeError: {output_text}") # Afficher le texte brut qui a causé l'erreur JSON
122
  return {"error": "JSON invalide", "raw": output_text}
123
  except Exception as e:
124
  return {"error": str(e)}
125
 
126
+ def download_and_extract_zip_from_hf_dataset(dataset_repo_id, zip_filename, target_folder):
127
  """
128
+ Télécharge un fichier ZIP depuis un Hugging Face Dataset et extrait les PDF
129
+ dans un dossier cible. Crée le dossier cible s'il n'existe pas.
130
  """
131
+ print(f"Tentative de téléchargement du fichier '{zip_filename}' "
132
+ f"depuis le dataset Hugging Face : {dataset_repo_id}")
133
  try:
134
+ # Télécharger le fichier ZIP depuis le dataset Hugging Face Hub
135
+ local_zip_path = hf_hub_download(
136
+ repo_id=dataset_repo_id,
137
+ filename=zip_filename,
138
+ repo_type="dataset",
139
+ cache_dir="./hf_cache" # Vous pouvez spécifier un répertoire de cache
140
+ )
141
+ print(f"Fichier ZIP téléchargé localement : {local_zip_path}")
142
+
143
  # Créer le dossier cible s'il n'existe pas
144
  Path(target_folder).mkdir(parents=True, exist_ok=True)
145
 
 
 
 
146
  extracted_files = []
147
+ with zipfile.ZipFile(local_zip_path, 'r') as z:
148
+ for file_info in z.infolist():
149
+ # Construire le chemin complet du fichier extrait
150
+ # Éviter les chemins absolus ou les traversées de répertoire pour la sécurité
151
+ if file_info.filename.startswith('/') or '..' in file_info.filename:
152
+ print(f"Skipping potentially unsafe path: {file_info.filename}")
153
+ continue
154
+
155
+ extracted_path = Path(target_folder) / file_info.filename
156
+
157
+ # Créer les sous-dossiers si nécessaire
158
+ if file_info.is_dir():
159
+ extracted_path.mkdir(parents=True, exist_ok=True)
160
+ elif file_info.filename.lower().endswith('.pdf'):
161
+ # Extraire uniquement les fichiers PDF
162
+ print(f"Extraction de : {file_info.filename} vers {extracted_path}")
163
+ try:
164
+ with extracted_path.open("wb") as outfile:
165
+ outfile.write(z.read(file_info.filename))
166
+ extracted_files.append(extracted_path)
167
+ except Exception as e:
168
+ print(f"Erreur lors de l'extraction de {file_info.filename}: {e}")
169
+ else:
170
+ print(f"Ignoré (non PDF) : {file_info.filename}")
171
 
172
  print(f"Extraction terminée. {len(extracted_files)} fichiers PDF extraits.")
173
  return extracted_files
174
 
 
 
 
 
 
 
175
  except Exception as e:
176
+ print(f"Une erreur est survenue lors du téléchargement ou de l'extraction depuis Hugging Face Dataset : {e}")
177
  return []
178
+ finally:
179
+ # Optionnel: supprimer le fichier ZIP téléchargé après l'extraction pour économiser de l'espace
180
+ # Attention: cela peut être utile si le Space a des limitations de stockage éphémère strictes.
181
+ # Si le fichier est grand, il pourrait être mis en cache et réutilisé par Hugging Face Hub.
182
+ # if Path(local_zip_path).exists():
183
+ # print(f"Suppression du fichier ZIP temporaire : {local_zip_path}")
184
+ # os.remove(local_zip_path)
185
+ pass # Ne rien faire ici, car hf_hub_download gère son propre cache
186
 
187
  # --- Fonction principale ---
188
  def main():
189
  """Fonction principale pour exécuter l'ensemble du processus d'extraction."""
190
+ # 1. Télécharger et extraire les PDF depuis le Hugging Face Dataset
191
+ downloaded_pdfs = download_and_extract_zip_from_hf_dataset(
192
+ HF_DATASET_REPO_ID, ZIP_FILENAME_IN_DATASET, PDF_FOLDER
193
+ )
194
 
195
  if not downloaded_pdfs:
196
  print("Aucun fichier PDF n'a pu être téléchargé ou extrait. Arrêt du processus.")
requirements.txt CHANGED
@@ -3,4 +3,4 @@ torch
3
  Pillow
4
  PyMuPDF
5
  tqdm
6
- requests
 
3
  Pillow
4
  PyMuPDF
5
  tqdm
6
+ huggingface_hub