IO4IT 4.0.7__tar.gz → 4.0.9__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- io4it-4.0.9/PKG-INFO +12 -0
- io4it-4.0.9/io4it.egg-info/PKG-INFO +12 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/toolViews/key_manager_ui.py +1 -1
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/utils/keys_manager.py +29 -10
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/utils/mail.py +54 -21
- io4it-4.0.9/orangecontrib/IO4IT/utils/pptx2jpg.py +102 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/widgets/OWInboxMailMonitoring.py +1 -1
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/widgets/OWPipAuditor.py +68 -41
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/widgets/OWXLSX_insert.py +134 -60
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/widgets/designer/owinsertdatatoexcel.ui +27 -14
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/widgets/designer/owpipauditor.ui +15 -2
- {io4it-4.0.7 → io4it-4.0.9}/setup.py +1 -1
- io4it-4.0.7/PKG-INFO +0 -30
- io4it-4.0.7/io4it.egg-info/PKG-INFO +0 -30
- io4it-4.0.7/orangecontrib/IO4IT/utils/pptx2jpg.py +0 -64
- {io4it-4.0.7 → io4it-4.0.9}/io4it.egg-info/SOURCES.txt +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/io4it.egg-info/dependency_links.txt +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/io4it.egg-info/entry_points.txt +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/io4it.egg-info/namespace_packages.txt +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/io4it.egg-info/requires.txt +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/io4it.egg-info/top_level.txt +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/__init__.py +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/ocr_function/__init__.py +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/ocr_function/word_converter.py +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/toolViews/designer/key_manager_ui.ui +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/toolViews/designer/link_workflow_manager_ui.ui +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/toolViews/linkHTLMWorkflow_manager_ui.py +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/utils/__init__.py +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/utils/environment_collector.py +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/utils/pool_exec_utils.py +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/utils/secret_manager.py +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/utils/utils_md.py +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/widgets/OWChatGpt.py +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/widgets/OWDeep_Search.py +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/widgets/OWDoclingToMarkdown.py +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/widgets/OWExportMarkdown.py +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/widgets/OWExtractTablesDocxToXlsx.py +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/widgets/OWJsonToDataTable.py +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/widgets/OWMD2HTML.py +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/widgets/OWMarkdownLoader.py +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/widgets/OWMarkdownizer.py +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/widgets/OWOfficeNormalizer.py +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/widgets/OWPPTX2JPG.py +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/widgets/OWParserHTML.py +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/widgets/OWPdfType.py +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/widgets/OWProcessPoolExecutor.py +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/widgets/OWS3Uploader.py +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/widgets/OWS3downloader.py +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/widgets/OWS3list.py +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/widgets/OWWebSearch.py +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/widgets/OWmailLoader.py +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/widgets/OWmailSender.py +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/widgets/OWwordpdf2docx.py +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/widgets/__init__.py +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/widgets/designer/__init__.py +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/widgets/designer/nogui.ui +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/widgets/designer/ow_extract_tables_docx_to_xlsx.ui +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/widgets/designer/ow_file_ext_selector.ui +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/widgets/designer/ow_json_to_data_table.ui +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/widgets/designer/owchatgpt.ui +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/widgets/designer/owdeepsearch.ui +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/widgets/designer/owdoclingasr.ui +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/widgets/designer/owdoclingtomarkdown.ui +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/widgets/designer/owexportmarkdown.ui +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/widgets/designer/owinboxmailmonitoring.ui +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/widgets/designer/owmailloader.ui +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/widgets/designer/owmailsender.ui +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/widgets/designer/owmarkdownizer.ui +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/widgets/designer/owmarkdownloader.ui +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/widgets/designer/owmd2html.ui +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/widgets/designer/owofficenormalizer.ui +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/widgets/designer/owparserhtml.ui +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/widgets/designer/owpdftype.ui +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/widgets/designer/owpptx2jpg.ui +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/widgets/designer/owprocesspoolexecutor.ui +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/widgets/designer/owspeechtotext.ui +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/widgets/designer/owvisualizationer.ui +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/widgets/designer/owwebsearch.ui +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/widgets/designer/wordpdf2docx.ui +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/widgets/icons/__init__.py +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/widgets/icons/chatgpt.png +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/widgets/icons/check_pdf.png +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/widgets/icons/deepsearch.svg +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/widgets/icons/dep_md_old.png +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/widgets/icons/download.png +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/widgets/icons/export_md.png +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/widgets/icons/extract_table.png +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/widgets/icons/file_extensor.png +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/widgets/icons/html.png +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/widgets/icons/json-file.png +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/widgets/icons/list_aws.png +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/widgets/icons/load_md.png +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/widgets/icons/mail_loader.png +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/widgets/icons/mail_writer.png +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/widgets/icons/md.png +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/widgets/icons/monitor-email.svg +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/widgets/icons/office_normalizer.png +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/widgets/icons/owmd2html.svg +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/widgets/icons/pip_auditor.png +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/widgets/icons/pptx2jpg.png +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/widgets/icons/process_pool_executor.png +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/widgets/icons/speech_to_text.png +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/widgets/icons/upload.png +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/widgets/icons/visualizationer.png +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/widgets/icons/websearch.png +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/widgets/icons/wordpdf2docx.png +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/widgets/icons/xlsx_insert.png +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/IO4IT/widgets/icons_dev/__init__.py +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/orangecontrib/__init__.py +0 -0
- {io4it-4.0.7 → io4it-4.0.9}/setup.cfg +0 -0
io4it-4.0.9/PKG-INFO
ADDED
|
@@ -155,7 +155,7 @@ class KeyManagerUI(QtWidgets.QDialog):
|
|
|
155
155
|
if func_name=="Create / overwrite api key":
|
|
156
156
|
self.textDetails.setText("{\n'service': 'service_name',\n'api_key': 'api_key',\n'description': 'this_is_a_description'\n}")
|
|
157
157
|
elif func_name=="Create / overwrite imap4_ssl configuration":
|
|
158
|
-
self.textDetails.setText("{\n'agent': 'service_name',\n'alias': 'alias_name',\n'domain': '@domain.com',\n'interval_second': 1,\n'password': 'password'\n}")
|
|
158
|
+
self.textDetails.setText("{\n'agent': 'service_name',\n'alias': 'alias_name',\n'domain': '@domain.com',\n'interval_second': 1,\n'password': 'password',\n'server_imap': 'imap.gmail.com',\n'port_imap': 993,\n'server_smtp': 'smtp.gmail.com',\n'port_smtp': 587\n}")
|
|
159
159
|
elif func_name=="Create / overwrite nxp configuration":
|
|
160
160
|
self.textDetails.setText("{\n'dossier_node_id': 'bode_key',\n'serveur': 'servor_adress',\n'username': 'my_username',\n'password': 'password',\n'description': 'this is a description'\n}")
|
|
161
161
|
elif func_name=="Create / overwrite owa configuration":
|
|
@@ -35,7 +35,7 @@ def ask_secure():
|
|
|
35
35
|
if resp in ("n", "no"):
|
|
36
36
|
return False
|
|
37
37
|
print("Please enter Y or N.")
|
|
38
|
-
#
|
|
38
|
+
# FONCTIONS D'OBFUSCATION GÉNÉRALES & CHEMIN
|
|
39
39
|
# Fonction pour générer une clé simple à partir du nom d'utilisateur
|
|
40
40
|
def get_user_key():
|
|
41
41
|
try:
|
|
@@ -115,7 +115,7 @@ def lire_list_email(chemin_fichier):
|
|
|
115
115
|
return None
|
|
116
116
|
|
|
117
117
|
|
|
118
|
-
def enregistrer_config_imap4_ssl(agent, my_domain, password, interval_second, alias=""):
|
|
118
|
+
def enregistrer_config_imap4_ssl(agent, my_domain, password, interval_second, alias="", server_imap="imap.gmail.com", port_imap=993, server_smtp="smtp.gmail.com", port_smtp=587):
|
|
119
119
|
try:
|
|
120
120
|
dossier = get_keys_dir("IMAP4_SSL")
|
|
121
121
|
# Crée le dossier s'il n'existe pas
|
|
@@ -137,7 +137,11 @@ def enregistrer_config_imap4_ssl(agent, my_domain, password, interval_second, al
|
|
|
137
137
|
"domain": my_domain,
|
|
138
138
|
"interval_second": interval_second,
|
|
139
139
|
"password_encrypted": mdp_chiffre,
|
|
140
|
-
"alias": alias
|
|
140
|
+
"alias": alias,
|
|
141
|
+
"server_imap": server_imap,
|
|
142
|
+
"port_imap": port_imap,
|
|
143
|
+
"server_smtp": server_smtp,
|
|
144
|
+
"port_smtp": port_smtp
|
|
141
145
|
}
|
|
142
146
|
|
|
143
147
|
# Écriture du fichier
|
|
@@ -151,7 +155,7 @@ def enregistrer_config_imap4_ssl(agent, my_domain, password, interval_second, al
|
|
|
151
155
|
print(f"❌ Erreur lors de l'enregistrement : {e}")
|
|
152
156
|
return 1
|
|
153
157
|
|
|
154
|
-
def enregistrer_config_imap4_ssl_secure(agent, my_domain, password, interval_second, alias=""):
|
|
158
|
+
def enregistrer_config_imap4_ssl_secure(agent, my_domain, password, interval_second, alias="", server_imap="imap.gmail.com", port_imap=993, server_smtp="smtp.gmail.com", port_smtp=587):
|
|
155
159
|
try:
|
|
156
160
|
dossier = get_keys_dir("IMAP4_SSL")
|
|
157
161
|
# Crée le dossier s'il n'existe pas
|
|
@@ -171,7 +175,11 @@ def enregistrer_config_imap4_ssl_secure(agent, my_domain, password, interval_sec
|
|
|
171
175
|
"domain": my_domain,
|
|
172
176
|
"interval_second": interval_second,
|
|
173
177
|
"password": password,
|
|
174
|
-
"alias": alias
|
|
178
|
+
"alias": alias,
|
|
179
|
+
"server_imap": server_imap,
|
|
180
|
+
"port_imap": port_imap,
|
|
181
|
+
"server_smtp": server_smtp,
|
|
182
|
+
"port_smtp": port_smtp
|
|
175
183
|
}
|
|
176
184
|
sm = secret_manager.SecretManager(service_name)
|
|
177
185
|
sm.store(contenu,overwrite=True)
|
|
@@ -326,7 +334,6 @@ def lire_config_imap4_ssl_dict_sec(chemin_fichier):
|
|
|
326
334
|
|
|
327
335
|
# Fonction pour lire le fichier de configuration et déchiffrer le mot de passe
|
|
328
336
|
def lire_config_imap4_ssl(chemin_fichier):
|
|
329
|
-
# renvoie une liste =["agent","domain",mdp,"interval_second"]
|
|
330
337
|
if len(chemin_fichier)<5:
|
|
331
338
|
print(f"❌ Error path not correct {chemin_fichier}")
|
|
332
339
|
return None
|
|
@@ -341,7 +348,11 @@ def lire_config_imap4_ssl(chemin_fichier):
|
|
|
341
348
|
contenu["domain"],
|
|
342
349
|
contenu["password"],
|
|
343
350
|
int(contenu["interval_second"]),
|
|
344
|
-
contenu.get("alias", "")
|
|
351
|
+
contenu.get("alias", ""),
|
|
352
|
+
contenu.get("server_imap", "imap.gmail.com"),
|
|
353
|
+
contenu.get("port_imap", 993),
|
|
354
|
+
contenu.get("server_smtp", "smtp.gmail.com"),
|
|
355
|
+
contenu.get("port_smtp", 587)
|
|
345
356
|
]
|
|
346
357
|
|
|
347
358
|
except Exception as e:
|
|
@@ -363,7 +374,11 @@ def lire_config_imap4_ssl(chemin_fichier):
|
|
|
363
374
|
contenu["domain"],
|
|
364
375
|
mdp_dechiffre,
|
|
365
376
|
int(contenu["interval_second"]),
|
|
366
|
-
contenu.get("alias", "")
|
|
377
|
+
contenu.get("alias", ""),
|
|
378
|
+
contenu.get("server_imap", "imap.gmail.com"),
|
|
379
|
+
contenu.get("port_imap", 993),
|
|
380
|
+
contenu.get("server_smtp", "smtp.gmail.com"),
|
|
381
|
+
contenu.get("port_smtp", 587)
|
|
367
382
|
]
|
|
368
383
|
except Exception as e:
|
|
369
384
|
print(f"❌ Erreur lors de la lecture : {e}")
|
|
@@ -549,15 +564,19 @@ def enregistrer_config_cli_imap4_ssl():
|
|
|
549
564
|
print("\n📝 Écriture d’un fichier de configuration :")
|
|
550
565
|
agent = input("🤖 Nom de l’agent : ").strip()
|
|
551
566
|
domaine = input("📨 @domain.com? : ").strip()
|
|
567
|
+
server_imap = input("📥 Serveur IMAP (ex: imap.nom.com) : ").strip()
|
|
568
|
+
port_imap = int(input("🔌 Port IMAP (ex: 993) : ").strip())
|
|
569
|
+
server_smtp = input("📤 Serveur SMTP (ex: smtp.nom.com) : ").strip()
|
|
570
|
+
port_smtp = int(input("🔌 Port SMTP (ex: 587) : ").strip())
|
|
552
571
|
mdp = input("📨mot de passe? : ").strip()
|
|
553
572
|
interval = int(input("⏱️ Intervalle en secondes : ").strip())
|
|
554
573
|
alias = input("Nom de l'alias : ").strip()
|
|
555
574
|
store = ask_secure()
|
|
556
575
|
if not store:
|
|
557
|
-
if 0 != enregistrer_config_imap4_ssl(agent, domaine, mdp, interval, alias):
|
|
576
|
+
if 0 != enregistrer_config_imap4_ssl(agent, domaine, mdp, interval, alias, server_imap, port_imap, server_smtp, port_smtp):
|
|
558
577
|
print("erreur!")
|
|
559
578
|
return
|
|
560
|
-
if 0 != enregistrer_config_imap4_ssl_secure(agent, domaine, mdp, interval, alias):
|
|
579
|
+
if 0 != enregistrer_config_imap4_ssl_secure(agent, domaine, mdp, interval, alias, server_imap, port_imap, server_smtp, port_smtp):
|
|
561
580
|
print("erreur!")
|
|
562
581
|
return
|
|
563
582
|
|
|
@@ -29,7 +29,6 @@ else:
|
|
|
29
29
|
from orangecontrib.AAIT.utils import MetManagement
|
|
30
30
|
from orangecontrib.IO4IT.utils import keys_manager
|
|
31
31
|
|
|
32
|
-
|
|
33
32
|
def clean_addresses(field,myemail):
|
|
34
33
|
"""Retourne une liste d'adresses nettoyées sans ton adresse"""
|
|
35
34
|
if not field:
|
|
@@ -37,6 +36,13 @@ def clean_addresses(field,myemail):
|
|
|
37
36
|
addresses = email.utils.getaddresses([field])
|
|
38
37
|
return [addr for name, addr in addresses if addr.lower() != myemail.lower()]
|
|
39
38
|
|
|
39
|
+
def get_addresses(field):
|
|
40
|
+
"""Retourne une liste d'adresses nettoyées sans ton adresse"""
|
|
41
|
+
if not field:
|
|
42
|
+
return []
|
|
43
|
+
addresses = email.utils.getaddresses([field])
|
|
44
|
+
return [addr for name, addr in addresses]
|
|
45
|
+
|
|
40
46
|
def mail_in_folder(agent_name, type="in"):
|
|
41
47
|
if agent_name is None or agent_name == "":
|
|
42
48
|
print("agent_name doit etre renseigné")
|
|
@@ -74,12 +80,15 @@ def mark_as_read(item):
|
|
|
74
80
|
return False
|
|
75
81
|
|
|
76
82
|
|
|
77
|
-
def check_new_emails(offusc_conf_agent,type_co,
|
|
83
|
+
def check_new_emails(offusc_conf_agent,type_co,list_agent_email=[]):
|
|
78
84
|
if type_co=="IMAP4_SSL":
|
|
79
85
|
try:
|
|
80
|
-
agent,my_domain,password,interl_seconds,alias=keys_manager.lire_config_imap4_ssl(offusc_conf_agent)
|
|
81
|
-
|
|
82
|
-
|
|
86
|
+
agent,my_domain,password,interl_seconds,alias,server_imap,port_imap,_,_=keys_manager.lire_config_imap4_ssl(offusc_conf_agent)
|
|
87
|
+
if(alias and alias != ""):
|
|
88
|
+
myemail=alias + my_domain
|
|
89
|
+
else:
|
|
90
|
+
myemail=agent + my_domain
|
|
91
|
+
imap = imaplib.IMAP4_SSL(server_imap,port_imap)
|
|
83
92
|
imap.login(myemail, password)
|
|
84
93
|
imap.select("inbox")
|
|
85
94
|
status, messages = imap.search(None, 'UNSEEN')
|
|
@@ -110,16 +119,29 @@ def check_new_emails(offusc_conf_agent,type_co, list_agent_email=[]):
|
|
|
110
119
|
|
|
111
120
|
# Expéditeur
|
|
112
121
|
from_ = msg.get("From")
|
|
122
|
+
|
|
113
123
|
# Destinataires
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
124
|
+
# Je n'ai pas compris la logique
|
|
125
|
+
#to_emails = clean_addresses(msg.get("To", ""),myemail)
|
|
126
|
+
#cc_emails = clean_addresses(msg.get("Cc", ""),myemail)
|
|
127
|
+
|
|
128
|
+
#if (to_emails != [] or cc_emails != []) and alias == "":
|
|
129
|
+
# if myemail not in to_emails and myemail not in cc_emails:
|
|
130
|
+
# print(f"l'adresse de reception est un alias : {to_emails}")
|
|
131
|
+
# continue
|
|
132
|
+
#if (to_emails == [] and cc_emails == []) and alias != "":
|
|
133
|
+
# print(f"l'adresse de reception est un alias : {alias}")
|
|
134
|
+
# continue
|
|
135
|
+
to_emails = get_addresses(msg.get("To", ""))
|
|
136
|
+
cc_emails = get_addresses(msg.get("Cc", ""))
|
|
137
|
+
all_recipients = to_emails + cc_emails
|
|
138
|
+
myemail = myemail.lower().strip()
|
|
139
|
+
all_recipients = [x.lower().strip() for x in all_recipients]
|
|
140
|
+
|
|
141
|
+
if myemail not in all_recipients:
|
|
142
|
+
print(f"Le mail n'est pas destiné à {myemail}")
|
|
122
143
|
continue
|
|
144
|
+
|
|
123
145
|
## passe en lu si ce n'est pas un alias
|
|
124
146
|
imap.store(mail_id, '+FLAGS', '\\Seen')
|
|
125
147
|
# Corps
|
|
@@ -634,20 +656,25 @@ def lire_message(chemin_fichier):
|
|
|
634
656
|
return donnees
|
|
635
657
|
|
|
636
658
|
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
def send_mail(expediteur, offusc_conf_agent, destinataire, sujet, contenu_html, piece_jointe_paths=None, serveur="smtp.gmail.com", port=587):
|
|
659
|
+
def send_mail(expediteur, offusc_conf_agent, destinataire, sujet, contenu_html, piece_jointe_paths=None, cc=None, serveur="smtp.gmail.com", port=587):
|
|
640
660
|
msg = EmailMessage()
|
|
641
661
|
msg['From'] = expediteur
|
|
642
662
|
msg['To'] = destinataire
|
|
643
663
|
msg['Subject'] = sujet
|
|
644
664
|
|
|
665
|
+
if cc:
|
|
666
|
+
# Si cc est une liste, on la transforme en chaîne séparée par des virgules
|
|
667
|
+
if isinstance(cc, list):
|
|
668
|
+
msg['Cc'] = ", ".join(cc)
|
|
669
|
+
else:
|
|
670
|
+
msg['Cc'] = cc
|
|
671
|
+
|
|
645
672
|
# ✅ Définir le contenu HTML comme contenu alternatif
|
|
646
673
|
msg.set_content("Votre client mail ne supporte pas le HTML.") # fallback texte brut
|
|
647
674
|
msg.add_alternative(contenu_html, subtype='html')
|
|
648
675
|
|
|
649
676
|
# ✅ Ajout d'une ou plusieurs pièces jointes si fournie
|
|
650
|
-
if piece_jointe_paths:
|
|
677
|
+
if piece_jointe_paths and os.path.exists(piece_jointe_paths):
|
|
651
678
|
piece_paths = [f for f in os.listdir(piece_jointe_paths) if os.path.isfile(os.path.join(piece_jointe_paths, f))]
|
|
652
679
|
for piece_path in piece_paths:
|
|
653
680
|
try:
|
|
@@ -660,7 +687,7 @@ def send_mail(expediteur, offusc_conf_agent, destinataire, sujet, contenu_html,
|
|
|
660
687
|
except Exception as e:
|
|
661
688
|
print(f"Erreur lors de l'ajout de la pièce jointe '{piece_path}': {e}")
|
|
662
689
|
try:
|
|
663
|
-
_, _, mot_de_passe, _, _ = keys_manager.lire_config_imap4_ssl(offusc_conf_agent)
|
|
690
|
+
_, _, mot_de_passe, _, _, _, _, _, _ = keys_manager.lire_config_imap4_ssl(offusc_conf_agent)
|
|
664
691
|
with smtplib.SMTP(serveur, port) as smtp:
|
|
665
692
|
smtp.starttls()
|
|
666
693
|
smtp.login(expediteur, mot_de_passe)
|
|
@@ -705,8 +732,9 @@ def to_graph_recipients(champ):
|
|
|
705
732
|
|
|
706
733
|
def check_send_new_emails(offusc_conf_agent, type_co):
|
|
707
734
|
if type_co == "IMAP4_SSL":
|
|
708
|
-
agent, domain, _, _, alias = keys_manager.lire_config_imap4_ssl(offusc_conf_agent)
|
|
735
|
+
agent, domain, _, _, alias, _, _, server_smtp, port_smtp = keys_manager.lire_config_imap4_ssl(offusc_conf_agent)
|
|
709
736
|
mail = agent + domain
|
|
737
|
+
|
|
710
738
|
if alias != "":
|
|
711
739
|
agent = alias
|
|
712
740
|
chemin_dossier_in, chemin_dossier_out = mail_in_folder(agent, "out")
|
|
@@ -725,14 +753,19 @@ def check_send_new_emails(offusc_conf_agent, type_co):
|
|
|
725
753
|
print("Aucun destinataire trouvé dans 'eme'")
|
|
726
754
|
continue
|
|
727
755
|
|
|
756
|
+
chemin_pj = chemin_dossier_out + "/" + contenu + "/pj"
|
|
757
|
+
pj_argument = chemin_pj if os.path.exists(chemin_pj) else None
|
|
758
|
+
|
|
728
759
|
send_mail(
|
|
729
760
|
mail,
|
|
730
761
|
offusc_conf_agent,
|
|
731
762
|
destinataires,
|
|
732
763
|
infos["tit"],
|
|
733
764
|
infos["txt"],
|
|
734
|
-
|
|
735
|
-
piece_jointe_paths=
|
|
765
|
+
cc=copies,
|
|
766
|
+
piece_jointe_paths=pj_argument,
|
|
767
|
+
serveur=server_smtp,
|
|
768
|
+
port=port_smtp
|
|
736
769
|
)
|
|
737
770
|
MetManagement.reset_folder(chemin_dossier_in + contenu, recreate=False)
|
|
738
771
|
MetManagement.reset_folder(chemin_dossier_out + contenu, recreate=False)
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
import time
|
|
3
|
+
import subprocess
|
|
4
|
+
import shutil
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
def get_conversion_method():
|
|
8
|
+
"""Determines the best available conversion method."""
|
|
9
|
+
# 1. Check for MS Office
|
|
10
|
+
if sys.platform.startswith("win"):
|
|
11
|
+
try:
|
|
12
|
+
import win32com.client
|
|
13
|
+
# Try to see if the COM object is registered
|
|
14
|
+
win32com.client.GetActiveObject("PowerPoint.Application")
|
|
15
|
+
return "office"
|
|
16
|
+
except:
|
|
17
|
+
try:
|
|
18
|
+
# If not running, try to see if it's dispatchable
|
|
19
|
+
win32com.client.Dispatch("PowerPoint.Application")
|
|
20
|
+
return "office"
|
|
21
|
+
except: pass
|
|
22
|
+
elif sys.platform == "darwin":
|
|
23
|
+
# Check if PowerPoint app exists on Mac
|
|
24
|
+
if Path("/Applications/Microsoft PowerPoint.app").exists():
|
|
25
|
+
return "office"
|
|
26
|
+
|
|
27
|
+
# 2. Check for LibreOffice (soffice)
|
|
28
|
+
if shutil.which("soffice"):
|
|
29
|
+
return "libreoffice"
|
|
30
|
+
|
|
31
|
+
# 3. Fallback to Aspose
|
|
32
|
+
return "aspose"
|
|
33
|
+
|
|
34
|
+
def convert_via_office(src: Path, pdf_path: Path):
|
|
35
|
+
if sys.platform.startswith("win"):
|
|
36
|
+
import win32com.client
|
|
37
|
+
powerpoint = win32com.client.Dispatch("PowerPoint.Application")
|
|
38
|
+
try:
|
|
39
|
+
presentation = powerpoint.Presentations.Open(str(src), WithWindow=False)
|
|
40
|
+
presentation.SaveAs(str(pdf_path), 32) # 32 = ppSaveAsPDF
|
|
41
|
+
presentation.Close()
|
|
42
|
+
finally:
|
|
43
|
+
# Note: We don't quit() powerpoint here to avoid killing other open docs
|
|
44
|
+
pass
|
|
45
|
+
elif sys.platform == "darwin":
|
|
46
|
+
script = f'tell application "Microsoft PowerPoint" to save (open POSIX file "{src}") in POSIX file "{pdf_path}" as PDF'
|
|
47
|
+
subprocess.run(["osascript", "-e", script], check=True)
|
|
48
|
+
|
|
49
|
+
def convert_via_libreoffice(src: Path, out_dir: Path):
|
|
50
|
+
subprocess.run([
|
|
51
|
+
"soffice", "--headless", "--convert-to", "pdf",
|
|
52
|
+
"--outdir", str(out_dir), str(src)
|
|
53
|
+
], check=True, capture_output=True)
|
|
54
|
+
return src.with_suffix(".pdf")
|
|
55
|
+
|
|
56
|
+
def process_one_pptx(file_path_str: str, unique_folder: bool = True):
|
|
57
|
+
try:
|
|
58
|
+
import fitz # PyMuPDF
|
|
59
|
+
except ImportError:
|
|
60
|
+
return [file_path_str, "", "nok", "0.0", "Erreur: PyMuPDF non installé"]
|
|
61
|
+
|
|
62
|
+
t0 = time.time()
|
|
63
|
+
src = Path(file_path_str).resolve()
|
|
64
|
+
out_dir = src.parent / (f"images_{src.stem}" if unique_folder else "images")
|
|
65
|
+
out_dir.mkdir(parents=True, exist_ok=True)
|
|
66
|
+
|
|
67
|
+
method = get_conversion_method()
|
|
68
|
+
img_list = []
|
|
69
|
+
|
|
70
|
+
try:
|
|
71
|
+
# --- PHASE 1: GENERATE IMAGES ---
|
|
72
|
+
if method == "aspose":
|
|
73
|
+
# import aspose.slides as slides
|
|
74
|
+
# with slides.Presentation(str(src)) as pres:
|
|
75
|
+
# for i, slide in enumerate(pres.slides, start=1):
|
|
76
|
+
# img_path = out_dir / f"{src.stem}_slide_{i:03d}.jpg"
|
|
77
|
+
# slide.get_thumbnail(1.5, 1.5).save(str(img_path), slides.export.ImageFormat.JPEG)
|
|
78
|
+
# img_list.append(str(img_path))
|
|
79
|
+
print("converting slides without having libre office or office isn't possible for the moment")
|
|
80
|
+
else:
|
|
81
|
+
# Office or LibreOffice both generate a PDF first
|
|
82
|
+
pdf_path = out_dir / f"{src.stem}.pdf"
|
|
83
|
+
if method == "office":
|
|
84
|
+
convert_via_office(src, pdf_path)
|
|
85
|
+
else: # libreoffice
|
|
86
|
+
convert_via_libreoffice(src, out_dir)
|
|
87
|
+
|
|
88
|
+
# Convert PDF pages to JPG using PyMuPDF
|
|
89
|
+
doc = fitz.open(str(pdf_path))
|
|
90
|
+
for i, page in enumerate(doc, start=1):
|
|
91
|
+
pix = page.get_pixmap(dpi=150)
|
|
92
|
+
img_path = out_dir / f"{src.stem}_slide_{i:03d}.jpg"
|
|
93
|
+
pix.save(str(img_path))
|
|
94
|
+
img_list.append(str(img_path))
|
|
95
|
+
doc.close()
|
|
96
|
+
if pdf_path.exists(): pdf_path.unlink()
|
|
97
|
+
|
|
98
|
+
duration = time.time() - t0
|
|
99
|
+
return [str(src), "|".join(img_list), "ok", f"{duration:.2f}", f"{len(img_list)} slides ({method})"]
|
|
100
|
+
|
|
101
|
+
except Exception as e:
|
|
102
|
+
return [str(src), "", "nok", f"{time.time()-t0:.2f}", f"Method {method} failed: {str(e)}"]
|
|
@@ -139,7 +139,7 @@ class OWInboxMailMonitoring(widget.OWWidget):
|
|
|
139
139
|
try:
|
|
140
140
|
agent = ""
|
|
141
141
|
if self.type_co == "IMAP4_SSL":
|
|
142
|
-
agent, _, _, _, alias = keys_manager.lire_config_imap4_ssl(self.your_email_conf)
|
|
142
|
+
agent, _, _, _, alias, _, _, _, _ = keys_manager.lire_config_imap4_ssl(self.your_email_conf)
|
|
143
143
|
if alias != "":
|
|
144
144
|
agent = alias
|
|
145
145
|
if self.type_co == "MICROSOFT_EXCHANGE_OWA":
|
|
@@ -5,9 +5,9 @@ from Orange.widgets import widget
|
|
|
5
5
|
from Orange.data import Domain, StringVariable, Table
|
|
6
6
|
from AnyQt.QtWidgets import QApplication
|
|
7
7
|
from Orange.widgets.settings import Setting
|
|
8
|
-
from Orange.widgets.utils.signals import Output
|
|
8
|
+
from Orange.widgets.utils.signals import Output, Input
|
|
9
9
|
from AnyQt.QtCore import QTimer
|
|
10
|
-
|
|
10
|
+
from datetime import datetime
|
|
11
11
|
|
|
12
12
|
# Importations conditionnelles selon l'architecture du projet
|
|
13
13
|
if "site-packages/Orange/widgets" in os.path.dirname(os.path.abspath(__file__)).replace("\\", "/"):
|
|
@@ -54,8 +54,6 @@ def run_audit(saved_packages: dict) -> tuple:
|
|
|
54
54
|
all_keys = set(saved_packages.keys()) | set(current_packages.keys())
|
|
55
55
|
|
|
56
56
|
diff_rows = []
|
|
57
|
-
current_rows = []
|
|
58
|
-
previous_rows = []
|
|
59
57
|
|
|
60
58
|
for pkg in sorted(all_keys):
|
|
61
59
|
saved_v = saved_packages.get(pkg, "(Absent)")
|
|
@@ -71,32 +69,21 @@ def run_audit(saved_packages: dict) -> tuple:
|
|
|
71
69
|
status = "✅ OK"
|
|
72
70
|
|
|
73
71
|
# Table différences : 4 colonnes complètes
|
|
74
|
-
#if status != "✅ OK":
|
|
75
72
|
diff_rows.append([pkg, saved_v, current_v, status])
|
|
76
73
|
|
|
77
|
-
# Table courante : uniquement Package + Version courante
|
|
78
|
-
if current_v != "(Nouveau)":
|
|
79
|
-
current_rows.append([pkg, current_v])
|
|
80
|
-
|
|
81
|
-
# Table précédente : uniquement Package + Version sauvegardée
|
|
82
|
-
if saved_v != "(Absent)":
|
|
83
|
-
previous_rows.append([pkg, saved_v])
|
|
84
74
|
|
|
85
75
|
diff_cols = ["Package", "Saved Version", "Current Version", "Status"]
|
|
86
|
-
|
|
87
|
-
previous_cols = ["Package", "Saved Version"]
|
|
76
|
+
|
|
88
77
|
|
|
89
78
|
diff_table = _make_table(diff_rows, diff_cols) if diff_rows else None
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
return diff_table, current_table, previous_table
|
|
79
|
+
|
|
80
|
+
return diff_table
|
|
94
81
|
|
|
95
82
|
|
|
96
83
|
# ---------------------------------------------------------------------------
|
|
97
84
|
# Widget
|
|
98
85
|
# ---------------------------------------------------------------------------
|
|
99
|
-
|
|
86
|
+
|
|
100
87
|
@apply_modification_from_python_file(filepath_original_widget=__file__)
|
|
101
88
|
class OWPipAuditor(widget.OWWidget):
|
|
102
89
|
name = "Pip Auditor"
|
|
@@ -115,16 +102,23 @@ class OWPipAuditor(widget.OWWidget):
|
|
|
115
102
|
# ------------------------------------------------------------------
|
|
116
103
|
# Settings persistants
|
|
117
104
|
# ------------------------------------------------------------------
|
|
118
|
-
saved_freeze
|
|
105
|
+
saved_freeze = Setting(None, schema_only=True) # Snapshot sauvegardé (dict pkg→version)
|
|
106
|
+
snapshot_time = Setting(None)
|
|
119
107
|
autorun: bool = Setting(False) # Checkbox "Lancer automatiquement au démarrage"
|
|
120
|
-
|
|
108
|
+
trigger_on_signal = Setting(False) # Checkbox "Run on signal"
|
|
109
|
+
# ------------------------------------------------------------------
|
|
110
|
+
# Entrées
|
|
111
|
+
# ------------------------------------------------------------------
|
|
112
|
+
class Inputs:
|
|
113
|
+
data = Input("Data", Orange.data.Table)
|
|
114
|
+
|
|
121
115
|
# ------------------------------------------------------------------
|
|
122
116
|
# Sorties
|
|
123
117
|
# ------------------------------------------------------------------
|
|
124
118
|
class Outputs:
|
|
125
|
-
diff_table = Output("
|
|
126
|
-
|
|
127
|
-
|
|
119
|
+
diff_table = Output("Results", Orange.data.Table) # Comparaison packages
|
|
120
|
+
info_table = Output("Infos", Orange.data.Table)
|
|
121
|
+
data = Output("Data", Orange.data.Table) # Sortie "Passthrough"
|
|
128
122
|
|
|
129
123
|
# ------------------------------------------------------------------
|
|
130
124
|
# Initialisation
|
|
@@ -145,6 +139,9 @@ class OWPipAuditor(widget.OWWidget):
|
|
|
145
139
|
# Checkbox autorun (doit être définie dans owpipauditor.ui avec le nom checkBox_autorun)
|
|
146
140
|
self.checkBox_autorun.setChecked(self.autorun)
|
|
147
141
|
self.checkBox_autorun.stateChanged.connect(self._on_autorun_changed)
|
|
142
|
+
|
|
143
|
+
self.checkBox_trigger.setChecked(self.trigger_on_signal)
|
|
144
|
+
self.checkBox_trigger.stateChanged.connect(self._on_trigger_changed)
|
|
148
145
|
|
|
149
146
|
self.post_initialized()
|
|
150
147
|
QTimer.singleShot(0, lambda: help_management.override_help_action(self))
|
|
@@ -152,6 +149,16 @@ class OWPipAuditor(widget.OWWidget):
|
|
|
152
149
|
# Démarrage automatique si l'option est activée et qu'un snapshot existe
|
|
153
150
|
if self.autorun and self.saved_freeze:
|
|
154
151
|
QTimer.singleShot(100, self.run)
|
|
152
|
+
|
|
153
|
+
@Inputs.data
|
|
154
|
+
def set_data(self, data):
|
|
155
|
+
# On renvoie immédiatement la donnée en sortie (Passthrough)
|
|
156
|
+
self.Outputs.data.send(data)
|
|
157
|
+
|
|
158
|
+
# Si on reçoit une donnée non nulle et que l'option est activée
|
|
159
|
+
if data is not None and self.trigger_on_signal:
|
|
160
|
+
# On lance la comparaison
|
|
161
|
+
self.run()
|
|
155
162
|
|
|
156
163
|
# ------------------------------------------------------------------
|
|
157
164
|
# Slots
|
|
@@ -159,11 +166,17 @@ class OWPipAuditor(widget.OWWidget):
|
|
|
159
166
|
def _on_autorun_changed(self, state: int):
|
|
160
167
|
"""Persistance de la checkbox autorun."""
|
|
161
168
|
self.autorun = bool(state)
|
|
169
|
+
|
|
170
|
+
def _on_trigger_changed(self, state: int):
|
|
171
|
+
self.trigger_on_signal = bool(state)
|
|
162
172
|
|
|
163
173
|
def take_snapshot(self):
|
|
164
174
|
"""Sauvegarde l'état pip courant dans le Setting."""
|
|
165
175
|
try:
|
|
166
|
-
self.saved_freeze = get_pip_freeze_dict()
|
|
176
|
+
self.saved_freeze = dict(get_pip_freeze_dict())
|
|
177
|
+
self.snapshot_time = datetime.now()
|
|
178
|
+
self.error("")
|
|
179
|
+
self.warning("")
|
|
167
180
|
self.information(
|
|
168
181
|
f"Snapshot réussi : {len(self.saved_freeze)} packages sauvegardés."
|
|
169
182
|
)
|
|
@@ -175,8 +188,8 @@ class OWPipAuditor(widget.OWWidget):
|
|
|
175
188
|
self.error("")
|
|
176
189
|
self.warning("")
|
|
177
190
|
|
|
178
|
-
if
|
|
179
|
-
self.error("Aucun snapshot sauvegardé.
|
|
191
|
+
if self.saved_freeze is None:
|
|
192
|
+
self.error("Aucun snapshot sauvegardé.")
|
|
180
193
|
return
|
|
181
194
|
|
|
182
195
|
if self.thread is not None:
|
|
@@ -197,27 +210,41 @@ class OWPipAuditor(widget.OWWidget):
|
|
|
197
210
|
self.progressBarSet(value)
|
|
198
211
|
|
|
199
212
|
def handle_result(self, result):
|
|
200
|
-
"""
|
|
201
|
-
result est un tuple (diff_table, current_table, previous_table).
|
|
202
|
-
"""
|
|
203
213
|
try:
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
214
|
+
self.clear_messages()
|
|
215
|
+
diff_table = result
|
|
216
|
+
|
|
217
|
+
if diff_table is None:
|
|
218
|
+
return
|
|
219
|
+
|
|
220
|
+
ref_time = self.snapshot_time
|
|
221
|
+
now_time = datetime.now()
|
|
222
|
+
|
|
223
|
+
info_rows = [[
|
|
224
|
+
ref_time.strftime("%Y-%m-%d %H:%M:%S") if ref_time else "N/A",
|
|
225
|
+
now_time.strftime("%Y-%m-%d %H:%M:%S"),
|
|
226
|
+
]]
|
|
227
|
+
|
|
228
|
+
info_table = _make_table(info_rows, ["Snapshot time", "Comparison time"])
|
|
229
|
+
|
|
207
230
|
self.Outputs.diff_table.send(diff_table)
|
|
208
|
-
self.Outputs.
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
n_diff = sum(1 for row in diff_table if str(row[
|
|
212
|
-
|
|
231
|
+
self.Outputs.info_table.send(info_table)
|
|
232
|
+
|
|
233
|
+
status_idx = diff_table.domain.metas.index(diff_table.domain["Status"])
|
|
234
|
+
n_diff = sum(1 for row in diff_table.metas if str(row[status_idx]) != "✅ OK")
|
|
235
|
+
|
|
236
|
+
self.information(
|
|
237
|
+
f"Audit terminé : {n_diff} différence(s)."
|
|
238
|
+
)
|
|
239
|
+
|
|
213
240
|
except Exception as e:
|
|
241
|
+
self.clear_messages()
|
|
214
242
|
self.error(f"Erreur lors de l'envoi des tables: {e}")
|
|
215
|
-
|
|
216
|
-
self.Outputs.current_table.send(None)
|
|
217
|
-
self.Outputs.previous_table.send(None)
|
|
243
|
+
|
|
218
244
|
|
|
219
245
|
def handle_finish(self):
|
|
220
246
|
self.progressBarFinished()
|
|
247
|
+
self.thread = None
|
|
221
248
|
|
|
222
249
|
# ------------------------------------------------------------------
|
|
223
250
|
# Hook post-init (override si besoin dans une sous-classe)
|