Dabococo commited on
Commit
fd19ee7
·
verified ·
1 Parent(s): 57aa78b

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +60 -44
app.py CHANGED
@@ -6,6 +6,7 @@ from io import BytesIO
6
  import pandas as pd
7
  import re
8
  import tempfile
 
9
 
10
  # Initialize Groq client
11
  client = Groq(api_key=os.getenv("GROQ_API_KEY"))
@@ -18,7 +19,7 @@ def image_to_base64(image):
18
 
19
  def parse_markdown_table_to_df(table_text):
20
  """Parse un tableau Markdown en Pandas DataFrame de manière robuste."""
21
- # Nettoyer les <br> ou \n pour les sauts de ligne dans les cellules
22
  table_text = re.sub(r'<br>', '\n', table_text)
23
 
24
  lines = table_text.split('\n')
@@ -49,11 +50,8 @@ def parse_markdown_table_to_df(table_text):
49
  # Extraire les lignes de données (lignes suivantes)
50
  rows = []
51
  for line in data_lines[1:]:
52
- cells = re.split(r'(?<!\\)\|', line)[1:-1] # Split sur | non échappé
53
- cleaned_cells = [cell.strip().replace("''", "").replace("'''", "").strip() for cell in cells] # Nettoyer '' et '''
54
- # Vérifier si la ligne est entièrement vide
55
- if all(cell == '' for cell in cleaned_cells):
56
- continue # Ignorer les lignes entièrement vides
57
  # Gérer le mismatch de colonnes
58
  if len(cleaned_cells) < num_columns:
59
  cleaned_cells.extend([''] * (num_columns - len(cleaned_cells)))
@@ -61,20 +59,39 @@ def parse_markdown_table_to_df(table_text):
61
  cleaned_cells = cleaned_cells[:num_columns] # Tronquer si trop de colonnes
62
  rows.append(cleaned_cells)
63
 
 
 
 
64
  # Créer le DataFrame
65
  df = pd.DataFrame(rows, columns=headers)
66
  return df if not df.empty else pd.DataFrame({"Erreur": ["Aucune donnée valide extraite"]})
67
 
68
- def extract_additional_text_and_table(response):
69
- """Extraire le texte additionnel et le tableau Markdown de la réponse structurée, de manière robuste."""
70
- # Chercher 'Additional text:' et 'Table:'
71
- additional_match = re.search(r'Additional text:(.*?)Table:', response, re.DOTALL | re.IGNORECASE)
72
- table_match = re.search(r'Table:(.*)', response, re.DOTALL | re.IGNORECASE)
73
-
74
- additional_text = additional_match.group(1).strip() if additional_match else ''
75
- table_text = table_match.group(1).strip() if table_match else response.strip()
76
-
77
- return additional_text, table_text
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
78
 
79
  def process_image_and_get_response(image):
80
  """Process the uploaded image, send to Groq vision model, parse response to table, and generate Excel."""
@@ -86,19 +103,21 @@ def process_image_and_get_response(image):
86
 
87
  # Prompt optimisé pour structure, précision et inclusion des infos supplémentaires
88
  prompt = (
89
- "Analyse l'image attentivement et extrait TOUT le contenu visible. "
90
- "D'abord, extrait TOUT texte additionnel autour, au-dessus, en-dessous ou à côté du tableau (titres, en-têtes de page, notes, pieds de page, logos, dates, signatures, etc.), en le recopiant mot pour mot, même si cela semble non structuré. Inclu tout ce qui n'est pas dans le tableau lui-même. "
91
- "Si il n'y a pas de texte additionnel, laisse 'Additional text:' vide. "
92
- "Ensuite, extrait le tableau en entier, en recopiant TOUTES les lignes et colonnes à l'identique, y compris les lignes vides ou partielles (mais marque les cellules vides avec ''). "
 
93
  "Utilise un format Markdown pour le tableau avec des | pour les colonnes et une ligne |---|---| pour les séparateurs. "
94
  "Assure-toi que CHAQUE ligne (en-têtes, séparateurs, données) a EXACTEMENT le même nombre de colonnes (compte les | : il doit y en avoir 10 pour 9 colonnes, incluant les | de début et fin). "
95
- "Pour les sauts de ligne dans une cellule, utilise \n. "
96
- "Ne remplace pas les cellules vides par quoi que ce soit d'autre que ''. "
97
  "N'ajoute aucun texte explicatif dans le tableau. "
98
  "Structure ta réponse exactement comme suit :\n"
99
- "Additional text: [tout le texte additionnel ici, séparé par \n si plusieurs lignes ; vide si rien]\n"
 
100
  "Table:\n"
101
- "[le tableau Markdown ici, sans lignes vides inutiles]"
102
  )
103
 
104
  try:
@@ -125,30 +144,27 @@ def process_image_and_get_response(image):
125
 
126
  response = completion.choices[0].message.content.strip()
127
 
128
- # Extraire texte additionnel et tableau
129
- additional_text, table_text = extract_additional_text_and_table(response)
130
 
131
  # Parse le tableau en DataFrame
132
  df = parse_markdown_table_to_df(table_text)
133
 
134
- excel_file = None
135
- with tempfile.NamedTemporaryFile(delete=False, suffix=".xlsx") as tmp:
136
- with pd.ExcelWriter(tmp.name, engine='openpyxl') as writer:
137
- # Feuille pour le tableau
138
- if not df.empty and "Erreur" not in df.columns:
139
- df.to_excel(writer, sheet_name='Tableau_Extrait', index=False)
140
- else:
141
- pd.DataFrame({"Message": ["Erreur lors du parsing du tableau"]}).to_excel(writer, sheet_name='Tableau_Extrait', index=False)
142
-
143
- # Feuille pour le texte additionnel, si présent et non vide
144
- if additional_text.strip():
145
- additional_lines = additional_text.split('\n')
146
- additional_df = pd.DataFrame({"Texte Additionnel": additional_lines})
147
- additional_df.to_excel(writer, sheet_name='Infos_Supplementaires', index=False)
148
-
149
- excel_file = tmp.name
150
-
151
- return response, excel_file
152
 
153
  except Exception as e:
154
  return f"Erreur : {str(e)}", None
 
6
  import pandas as pd
7
  import re
8
  import tempfile
9
+ import unicodedata # Pour sanitizer le nom de fichier
10
 
11
  # Initialize Groq client
12
  client = Groq(api_key=os.getenv("GROQ_API_KEY"))
 
19
 
20
  def parse_markdown_table_to_df(table_text):
21
  """Parse un tableau Markdown en Pandas DataFrame de manière robuste."""
22
+ # Nettoyer les <br> en \n pour les sauts de ligne dans les cellules
23
  table_text = re.sub(r'<br>', '\n', table_text)
24
 
25
  lines = table_text.split('\n')
 
50
  # Extraire les lignes de données (lignes suivantes)
51
  rows = []
52
  for line in data_lines[1:]:
53
+ cells = line.split('|')[1:-1]
54
+ cleaned_cells = [cell.strip() for cell in cells]
 
 
 
55
  # Gérer le mismatch de colonnes
56
  if len(cleaned_cells) < num_columns:
57
  cleaned_cells.extend([''] * (num_columns - len(cleaned_cells)))
 
59
  cleaned_cells = cleaned_cells[:num_columns] # Tronquer si trop de colonnes
60
  rows.append(cleaned_cells)
61
 
62
+ # Filtrer les lignes entièrement vides (tous '' ou vides)
63
+ rows = [row for row in rows if any(cell.strip() != '' for cell in row)]
64
+
65
  # Créer le DataFrame
66
  df = pd.DataFrame(rows, columns=headers)
67
  return df if not df.empty else pd.DataFrame({"Erreur": ["Aucune donnée valide extraite"]})
68
 
69
+ def extract_filename_additional_and_table(response):
70
+ """Extraire le nom de fichier, le texte additionnel et le tableau Markdown de la réponse structurée."""
71
+ filename = "tableau_extrait" # Default
72
+ additional_text = ""
73
+ table_text = ""
74
+
75
+ if 'Filename:' in response:
76
+ parts = response.split('Filename:', 1)[1].split('\n', 1)
77
+ filename = parts[0].strip().replace('.xlsx', '') # Enlever extension si présente
78
+ remaining = parts[1] if len(parts) > 1 else ""
79
+ else:
80
+ remaining = response
81
+
82
+ if 'Additional text:' in remaining:
83
+ parts = remaining.split('Additional text:', 1)[1].split('Table:', 1)
84
+ additional_text = parts[0].strip()
85
+ table_text = parts[1].strip() if len(parts) > 1 else ""
86
+ else:
87
+ table_text = remaining.strip()
88
+
89
+ # Sanitizer le nom de fichier : enlever accents, caractères spéciaux, limiter à alphanum + _ -
90
+ filename = ''.join(c for c in unicodedata.normalize('NFD', filename) if unicodedata.category(c) != 'Mn')
91
+ filename = re.sub(r'[^a-zA-Z0-9_-]', '_', filename)
92
+ filename = filename[:50] # Limiter la longueur
93
+
94
+ return filename, additional_text, table_text
95
 
96
  def process_image_and_get_response(image):
97
  """Process the uploaded image, send to Groq vision model, parse response to table, and generate Excel."""
 
103
 
104
  # Prompt optimisé pour structure, précision et inclusion des infos supplémentaires
105
  prompt = (
106
+ "Analyse l'image et extrait tout le contenu. "
107
+ "D'abord, suggère un nom de fichier descriptif pour l'Excel basé sur le contenu principal de l'image (court, sans extension, ex: 'Tableau_Dossiers_2022'). "
108
+ "Ensuite, extrait TOUT texte additionnel autour, au-dessus, en-dessous ou à côté du tableau (titres, en-têtes, notes, pieds de page, logos, etc.), en le recopiant mot pour mot, même si c'est dispersé. "
109
+ "Si aucun texte additionnel, laisse vide. "
110
+ "Enfin, extrait le tableau en entier, en recopiant TOUTES les lignes et colonnes à l'identique, y compris les lignes vides ou partielles si elles existent dans l'image. "
111
  "Utilise un format Markdown pour le tableau avec des | pour les colonnes et une ligne |---|---| pour les séparateurs. "
112
  "Assure-toi que CHAQUE ligne (en-têtes, séparateurs, données) a EXACTEMENT le même nombre de colonnes (compte les | : il doit y en avoir 10 pour 9 colonnes, incluant les | de début et fin). "
113
+ "Pour les sauts de ligne dans une cellule, utilise \n au lieu de <br>. "
114
+ "Remplis les cellules vides avec '' si nécessaire pour maintenir l'alignement, mais ne filtre pas les lignes vides si elles sont présentes. "
115
  "N'ajoute aucun texte explicatif dans le tableau. "
116
  "Structure ta réponse exactement comme suit :\n"
117
+ "Filename: [nom_suggéré]\n"
118
+ "Additional text: [tout le texte additionnel, séparé par \n si plusieurs lignes ; sinon vide]\n"
119
  "Table:\n"
120
+ "[le tableau Markdown ici]"
121
  )
122
 
123
  try:
 
144
 
145
  response = completion.choices[0].message.content.strip()
146
 
147
+ # Extraire nom de fichier, texte additionnel et tableau
148
+ filename, additional_text, table_text = extract_filename_additional_and_table(response)
149
 
150
  # Parse le tableau en DataFrame
151
  df = parse_markdown_table_to_df(table_text)
152
 
153
+ # Créer le fichier Excel avec nom personnalisé
154
+ excel_path = f"/tmp/{filename if filename else 'tableau_extrait'}.xlsx"
155
+ with pd.ExcelWriter(excel_path, engine='openpyxl') as writer:
156
+ # Feuille pour le tableau
157
+ if not df.empty and "Erreur" not in df.columns:
158
+ df.to_excel(writer, sheet_name='Tableau_Extrait', index=False)
159
+ else:
160
+ pd.DataFrame({"Message": ["Erreur lors du parsing du tableau"]}).to_excel(writer, sheet_name='Tableau_Extrait', index=False)
161
+
162
+ # Feuille pour le texte additionnel, si présent
163
+ if additional_text:
164
+ additional_df = pd.DataFrame({"Texte Additionnel": additional_text.split('\n')})
165
+ additional_df.to_excel(writer, sheet_name='Infos_Supplementaires', index=False)
166
+
167
+ return response, excel_path
 
 
 
168
 
169
  except Exception as e:
170
  return f"Erreur : {str(e)}", None