omarbajouk commited on
Commit
e75ca13
·
verified ·
1 Parent(s): 6c927e5

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +196 -2
app.py CHANGED
@@ -1,4 +1,198 @@
1
  ...
2
 
3
- if __name__ == "__main__":
4
- app.run(host="0.0.0.0", port=7860)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ...
2
 
3
+
4
+ import gradio as gr
5
+ import os
6
+ import shutil
7
+ import zipfile
8
+ import pytesseract
9
+ from pdf2image import convert_from_path
10
+ from PIL import Image
11
+ import re
12
+ from datetime import datetime
13
+
14
+ # Chemins des dossiers
15
+ UPLOAD_FOLDER = 'uploads'
16
+ RESULT_FOLDER = 'results'
17
+
18
+ # Créer les dossiers si nécessaire
19
+ os.makedirs(UPLOAD_FOLDER, exist_ok=True)
20
+ os.makedirs(RESULT_FOLDER, exist_ok=True)
21
+
22
+ # Fonction pour afficher la documentation directement dans l'interface
23
+ def afficher_doc():
24
+ return """
25
+ # 📄 Documentation - Traitement des Notifications du Comité de l’Action Sociale
26
+
27
+ Cet outil vous permet de :
28
+ - 🧠 Extraire automatiquement **nom**, **référence**, et **date de séance** des notifications PDF
29
+ - 🗂️ Renommer les fichiers selon le format : `NOM_PRENOM_ANNÉE-RÉF.pdf`
30
+ - 📆 Classer les fichiers dans des dossiers par **date de séance** (AAAAMMJJ)
31
+ - 🔁 Gérer les **doublons intelligemment**
32
+ - 🧾 Générer un **fichier log CSV** de suivi
33
+
34
+ 👤 *Conçu pour les collaborateurs du CPAS et les secrétariats du CPAS Bruxelles*
35
+
36
+ 📬 Contact : omar.bajouk@cpasbxl.brussels
37
+ """
38
+
39
+ def process_pdfs(zip_file):
40
+ # Configuration des dossiers
41
+ pdf_folder = "pdf_folder"
42
+ output_log = "rename_log.csv"
43
+
44
+ # Nettoyage des dossiers existants
45
+ if os.path.exists(pdf_folder):
46
+ shutil.rmtree(pdf_folder)
47
+ os.makedirs(pdf_folder, exist_ok=True)
48
+
49
+ # Extraction du ZIP
50
+ with zipfile.ZipFile(zip_file.name, 'r') as zip_ref:
51
+ zip_ref.extractall(pdf_folder)
52
+
53
+ # Patterns Regex
54
+ title_pattern = re.compile(
55
+ r"(?:Monsieur|Madame|Morveuwer|De heer)\s+((?:[\w\-éèêëàâäîïôöùûüç']+\s+){1,3}[\w\-éèêëàâäîïôöùûüç']+)",
56
+ re.IGNORECASE
57
+ )
58
+ seance_pattern = re.compile(r"SEANCE\s+du\s+(\d{2})[\/\-](\d{2})[\/\-](\d{4})", re.IGNORECASE)
59
+ ref_pattern = re.compile(r"n\.réf\s*[:\-]?\s*das\/(?:[\w]+\/)*(\d{4})\/(\d+)", re.IGNORECASE)
60
+
61
+ log_lines = ["original_filename,new_filename,date_folder"]
62
+ processed_files = []
63
+
64
+ for filename in os.listdir(pdf_folder):
65
+ if not filename.lower().endswith(".pdf"):
66
+ continue
67
+
68
+ filepath = os.path.join(pdf_folder, filename)
69
+ try:
70
+ # Conversion PDF -> Image -> Texte
71
+ images = convert_from_path(filepath, first_page=1, last_page=1)
72
+ image = images[0]
73
+
74
+ # Recadrer l'image pour ne traiter que la moitié supérieure de la page entière
75
+ width, height = image.size
76
+ half_height = height // 2 # Diviser la hauteur par 2 pour obtenir la moitié supérieure
77
+ cropped_image = image.crop((0, 0, width, half_height)) # Recadrage pour la moitié supérieure
78
+
79
+ # Appliquer OCR sur la moitié supérieure
80
+ text = pytesseract.image_to_string(cropped_image, lang='fra+eng')
81
+
82
+ # Extraction des informations
83
+ name_match = title_pattern.search(text)
84
+ safe_name = "NO_NAME"
85
+ if name_match:
86
+ name = name_match.group(1).strip()
87
+ safe_name = re.sub(r"[^\w]", "_", name)
88
+ safe_name = re.sub(r"_+", "_", safe_name)
89
+ safe_name = re.sub(r"(_?DEPARTEMENT|_?ACTION|_?DIRECTION|_?SERVICE|_?UNITE|_?DIVISION)+", "", safe_name, flags=re.IGNORECASE)
90
+ safe_name = safe_name.strip("_")
91
+
92
+ # Date
93
+ date_match = seance_pattern.search(text)
94
+ date_str = "NO_DATE"
95
+ if date_match:
96
+ day, month, year = date_match.groups()
97
+ date_str = f"{year}{month}{day}"
98
+ folder_path = os.path.join(pdf_folder, date_str)
99
+ os.makedirs(folder_path, exist_ok=True)
100
+ else:
101
+ folder_path = pdf_folder
102
+
103
+ # Référence
104
+ ref_match = ref_pattern.search(text)
105
+ ref_code = f"{ref_match.group(1)}-{ref_match.group(2)}" if ref_match else "NO_REF"
106
+
107
+ # Nouveau nom de fichier
108
+ new_filename = f"{safe_name}_{ref_code}.pdf"
109
+ new_path = os.path.join(folder_path, new_filename)
110
+
111
+ # STEP 2: Remove '_D' and 'NO_REF' from the filename
112
+ final_filename = re.sub(r"_D_", "_", new_filename) # Remove '_D' from the filename
113
+ final_filename = re.sub(r"NO_REF", "", final_filename) # Remove 'NO_REF' if present
114
+
115
+ # Optionally, you can also clean up any extra underscores left after removal
116
+ final_filename = re.sub(r"_{2,}", "_", final_filename) # Replace multiple underscores with a single one
117
+ final_filename = final_filename.strip("_") # Remove underscores at the start or end of the filename
118
+
119
+ # Gestion des doublons : Vérifier si le fichier existe déjà et ajouter un suffixe si nécessaire
120
+ base_filename, ext = os.path.splitext(final_filename)
121
+ counter = 1
122
+ while os.path.exists(os.path.join(folder_path, final_filename)):
123
+ final_filename = f"{base_filename}_{counter}{ext}"
124
+ counter += 1
125
+
126
+ # Renommer
127
+ if not os.path.exists(new_path):
128
+ os.rename(filepath, new_path)
129
+ final_path = os.path.join(folder_path, final_filename)
130
+
131
+ if final_path != new_path:
132
+ os.rename(new_path, final_path)
133
+ new_filename = final_filename
134
+
135
+ log_lines.append(f"{filename},{new_filename},{date_str}")
136
+ processed_files.append(f"- {filename} → {new_filename}")
137
+ else:
138
+ log_lines.append(f"{filename},DUPLICATE_SKIPPED,{date_str}")
139
+
140
+ except Exception as e:
141
+ log_lines.append(f"{filename},ERROR,NO_DATE")
142
+ processed_files.append(f"❌ {filename} (Erreur: {str(e)})")
143
+
144
+ # Génération des fichiers de sortie
145
+ with open(output_log, "w", encoding="utf-8") as f:
146
+ f.write("\n".join(log_lines))
147
+
148
+ shutil.make_archive("renamed_pdfs", 'zip', pdf_folder)
149
+
150
+ # Préparation du rapport
151
+ last_files = "\n".join(processed_files[-5:]) if processed_files else "Aucun fichier traité"
152
+ report = f"""
153
+ **Traitement terminé !**
154
+ - Fichiers traités : {len(log_lines)-1}
155
+ - Derniers fichiers :
156
+ {last_files}
157
+ """
158
+
159
+ return "renamed_pdfs.zip", output_log, report
160
+
161
+ # Interface Gradio
162
+ with gr.Blocks(title="Assistant de Traitement des Notifications PDF") as demo:
163
+ gr.Markdown("""
164
+ # 📄 Assistant – Traitement des Notifications du Conseil de l’Action Sociale
165
+
166
+ Téléchargez un fichier ZIP avec des PDFs pour traiter et organiser les notifications.
167
+ """)
168
+
169
+ # Ajouter un bouton pour afficher la documentation
170
+ btn_doc = gr.Button("📘 Voir la documentation")
171
+
172
+ # Zone où la documentation sera affichée
173
+ output_doc = gr.Markdown()
174
+
175
+ # Fonction pour afficher la documentation lorsque le bouton est cliqué
176
+ btn_doc.click(
177
+ fn=afficher_doc,
178
+ outputs=output_doc
179
+ )
180
+
181
+ with gr.Row():
182
+ input_zip = gr.File(label="1. Fichier ZIP contenant les PDF", file_types=[".zip"])
183
+ btn_process = gr.Button("🚀 Traiter les fichiers")
184
+
185
+ with gr.Row():
186
+ output_zip = gr.File(label="2. PDFs Renommés (ZIP)")
187
+ output_log = gr.File(label="3. Fichier Log (CSV)")
188
+
189
+ output_report = gr.Markdown()
190
+
191
+ btn_process.click(
192
+ fn=process_pdfs,
193
+ inputs=input_zip,
194
+ outputs=[output_zip, output_log, output_report]
195
+ )
196
+
197
+ demo.launch(share=True)
198
+