io4it 3.0.3.2__tar.gz → 3.0.4__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.
Files changed (94) hide show
  1. {io4it-3.0.3.2 → io4it-3.0.4}/PKG-INFO +1 -1
  2. {io4it-3.0.3.2 → io4it-3.0.4}/io4it.egg-info/PKG-INFO +1 -1
  3. {io4it-3.0.3.2 → io4it-3.0.4}/io4it.egg-info/SOURCES.txt +4 -1
  4. {io4it-3.0.3.2 → io4it-3.0.4}/orangecontrib/IO4IT/utils/keys_manager.py +138 -13
  5. {io4it-3.0.3.2 → io4it-3.0.4}/orangecontrib/IO4IT/utils/mail.py +11 -95
  6. {io4it-3.0.3.2 → io4it-3.0.4}/orangecontrib/IO4IT/widgets/OWExportMarkdown.py +12 -9
  7. {io4it-3.0.3.2 → io4it-3.0.4}/orangecontrib/IO4IT/widgets/OWExtractTablesDocxToXlsx.py +47 -43
  8. {io4it-3.0.3.2 → io4it-3.0.4}/orangecontrib/IO4IT/widgets/OWInboxMailMonitoring.py +9 -5
  9. io4it-3.0.4/orangecontrib/IO4IT/widgets/OWJsonToDataTable.py +80 -0
  10. io4it-3.0.3.2/orangecontrib/IO4IT/widgets/designer/owdocxtoxlsx.ui → io4it-3.0.4/orangecontrib/IO4IT/widgets/designer/ow_extract_tables_docx_to_xlsx.ui +34 -44
  11. io4it-3.0.4/orangecontrib/IO4IT/widgets/designer/ow_json_to_data_table.ui +138 -0
  12. io4it-3.0.4/orangecontrib/IO4IT/widgets/icons/json-file.png +0 -0
  13. {io4it-3.0.3.2 → io4it-3.0.4}/setup.py +1 -1
  14. {io4it-3.0.3.2 → io4it-3.0.4}/io4it.egg-info/dependency_links.txt +0 -0
  15. {io4it-3.0.3.2 → io4it-3.0.4}/io4it.egg-info/entry_points.txt +0 -0
  16. {io4it-3.0.3.2 → io4it-3.0.4}/io4it.egg-info/namespace_packages.txt +0 -0
  17. {io4it-3.0.3.2 → io4it-3.0.4}/io4it.egg-info/requires.txt +0 -0
  18. {io4it-3.0.3.2 → io4it-3.0.4}/io4it.egg-info/top_level.txt +0 -0
  19. {io4it-3.0.3.2 → io4it-3.0.4}/orangecontrib/IO4IT/__init__.py +0 -0
  20. {io4it-3.0.3.2 → io4it-3.0.4}/orangecontrib/IO4IT/ocr_function/__init__.py +0 -0
  21. {io4it-3.0.3.2 → io4it-3.0.4}/orangecontrib/IO4IT/ocr_function/word_converter.py +0 -0
  22. {io4it-3.0.3.2 → io4it-3.0.4}/orangecontrib/IO4IT/utils/__init__.py +0 -0
  23. {io4it-3.0.3.2 → io4it-3.0.4}/orangecontrib/IO4IT/utils/pool_exec_utils.py +0 -0
  24. {io4it-3.0.3.2 → io4it-3.0.4}/orangecontrib/IO4IT/utils/secret_manager.py +0 -0
  25. {io4it-3.0.3.2 → io4it-3.0.4}/orangecontrib/IO4IT/utils/utils_md.py +0 -0
  26. {io4it-3.0.3.2 → io4it-3.0.4}/orangecontrib/IO4IT/widgets/OWChatGpt.py +0 -0
  27. {io4it-3.0.3.2 → io4it-3.0.4}/orangecontrib/IO4IT/widgets/OWDeep_Search.py +0 -0
  28. {io4it-3.0.3.2 → io4it-3.0.4}/orangecontrib/IO4IT/widgets/OWDoclingASR.py +0 -0
  29. {io4it-3.0.3.2 → io4it-3.0.4}/orangecontrib/IO4IT/widgets/OWDoclingToMarkdown.py +0 -0
  30. {io4it-3.0.3.2 → io4it-3.0.4}/orangecontrib/IO4IT/widgets/OWMD2HTML.py +0 -0
  31. {io4it-3.0.3.2 → io4it-3.0.4}/orangecontrib/IO4IT/widgets/OWMarkdownLoader.py +0 -0
  32. {io4it-3.0.3.2 → io4it-3.0.4}/orangecontrib/IO4IT/widgets/OWMarkdownizer.py +0 -0
  33. {io4it-3.0.3.2 → io4it-3.0.4}/orangecontrib/IO4IT/widgets/OWOfficeNormalizer.py +0 -0
  34. {io4it-3.0.3.2 → io4it-3.0.4}/orangecontrib/IO4IT/widgets/OWParserHTML.py +0 -0
  35. {io4it-3.0.3.2 → io4it-3.0.4}/orangecontrib/IO4IT/widgets/OWPdfType.py +0 -0
  36. {io4it-3.0.3.2 → io4it-3.0.4}/orangecontrib/IO4IT/widgets/OWProcessPoolExecutor.py +0 -0
  37. {io4it-3.0.3.2 → io4it-3.0.4}/orangecontrib/IO4IT/widgets/OWS3Uploader.py +0 -0
  38. {io4it-3.0.3.2 → io4it-3.0.4}/orangecontrib/IO4IT/widgets/OWS3downloader.py +0 -0
  39. {io4it-3.0.3.2 → io4it-3.0.4}/orangecontrib/IO4IT/widgets/OWS3list.py +0 -0
  40. {io4it-3.0.3.2 → io4it-3.0.4}/orangecontrib/IO4IT/widgets/OWSpeechToText.py +0 -0
  41. {io4it-3.0.3.2 → io4it-3.0.4}/orangecontrib/IO4IT/widgets/OWWebSearch.py +0 -0
  42. {io4it-3.0.3.2 → io4it-3.0.4}/orangecontrib/IO4IT/widgets/OWmailLoader.py +0 -0
  43. {io4it-3.0.3.2 → io4it-3.0.4}/orangecontrib/IO4IT/widgets/OWmailSender.py +0 -0
  44. {io4it-3.0.3.2 → io4it-3.0.4}/orangecontrib/IO4IT/widgets/OWwordpdf2docx.py +0 -0
  45. {io4it-3.0.3.2 → io4it-3.0.4}/orangecontrib/IO4IT/widgets/__init__.py +0 -0
  46. {io4it-3.0.3.2 → io4it-3.0.4}/orangecontrib/IO4IT/widgets/designer/__init__.py +0 -0
  47. {io4it-3.0.3.2 → io4it-3.0.4}/orangecontrib/IO4IT/widgets/designer/nogui.ui +0 -0
  48. {io4it-3.0.3.2 → io4it-3.0.4}/orangecontrib/IO4IT/widgets/designer/ow_file_ext_selector.ui +0 -0
  49. {io4it-3.0.3.2 → io4it-3.0.4}/orangecontrib/IO4IT/widgets/designer/owchatgpt.ui +0 -0
  50. {io4it-3.0.3.2 → io4it-3.0.4}/orangecontrib/IO4IT/widgets/designer/owdeepsearch.ui +0 -0
  51. {io4it-3.0.3.2 → io4it-3.0.4}/orangecontrib/IO4IT/widgets/designer/owdoclingasr.ui +0 -0
  52. {io4it-3.0.3.2 → io4it-3.0.4}/orangecontrib/IO4IT/widgets/designer/owdoclingtomarkdown.ui +0 -0
  53. {io4it-3.0.3.2 → io4it-3.0.4}/orangecontrib/IO4IT/widgets/designer/owexportmarkdown.ui +0 -0
  54. {io4it-3.0.3.2 → io4it-3.0.4}/orangecontrib/IO4IT/widgets/designer/owinboxmailmonitoring.ui +0 -0
  55. {io4it-3.0.3.2 → io4it-3.0.4}/orangecontrib/IO4IT/widgets/designer/owmailloader.ui +0 -0
  56. {io4it-3.0.3.2 → io4it-3.0.4}/orangecontrib/IO4IT/widgets/designer/owmailsender.ui +0 -0
  57. {io4it-3.0.3.2 → io4it-3.0.4}/orangecontrib/IO4IT/widgets/designer/owmarkdownizer.ui +0 -0
  58. {io4it-3.0.3.2 → io4it-3.0.4}/orangecontrib/IO4IT/widgets/designer/owmarkdownloader.ui +0 -0
  59. {io4it-3.0.3.2 → io4it-3.0.4}/orangecontrib/IO4IT/widgets/designer/owmd2html.ui +0 -0
  60. {io4it-3.0.3.2 → io4it-3.0.4}/orangecontrib/IO4IT/widgets/designer/owofficenormalizer.ui +0 -0
  61. {io4it-3.0.3.2 → io4it-3.0.4}/orangecontrib/IO4IT/widgets/designer/owparserhtml.ui +0 -0
  62. {io4it-3.0.3.2 → io4it-3.0.4}/orangecontrib/IO4IT/widgets/designer/owpdftype.ui +0 -0
  63. {io4it-3.0.3.2 → io4it-3.0.4}/orangecontrib/IO4IT/widgets/designer/owprocesspoolexecutor.ui +0 -0
  64. {io4it-3.0.3.2 → io4it-3.0.4}/orangecontrib/IO4IT/widgets/designer/owspeechtotext.ui +0 -0
  65. {io4it-3.0.3.2 → io4it-3.0.4}/orangecontrib/IO4IT/widgets/designer/owvisualizationer.ui +0 -0
  66. {io4it-3.0.3.2 → io4it-3.0.4}/orangecontrib/IO4IT/widgets/designer/owwebsearch.ui +0 -0
  67. {io4it-3.0.3.2 → io4it-3.0.4}/orangecontrib/IO4IT/widgets/designer/wordpdf2docx.ui +0 -0
  68. {io4it-3.0.3.2 → io4it-3.0.4}/orangecontrib/IO4IT/widgets/icons/__init__.py +0 -0
  69. {io4it-3.0.3.2 → io4it-3.0.4}/orangecontrib/IO4IT/widgets/icons/chatgpt.png +0 -0
  70. {io4it-3.0.3.2 → io4it-3.0.4}/orangecontrib/IO4IT/widgets/icons/check_pdf.png +0 -0
  71. {io4it-3.0.3.2 → io4it-3.0.4}/orangecontrib/IO4IT/widgets/icons/deepsearch.svg +0 -0
  72. {io4it-3.0.3.2 → io4it-3.0.4}/orangecontrib/IO4IT/widgets/icons/dep_md_old.png +0 -0
  73. {io4it-3.0.3.2 → io4it-3.0.4}/orangecontrib/IO4IT/widgets/icons/download.png +0 -0
  74. {io4it-3.0.3.2 → io4it-3.0.4}/orangecontrib/IO4IT/widgets/icons/export_md.png +0 -0
  75. {io4it-3.0.3.2 → io4it-3.0.4}/orangecontrib/IO4IT/widgets/icons/extract_table.png +0 -0
  76. {io4it-3.0.3.2 → io4it-3.0.4}/orangecontrib/IO4IT/widgets/icons/file_extensor.png +0 -0
  77. {io4it-3.0.3.2 → io4it-3.0.4}/orangecontrib/IO4IT/widgets/icons/html.png +0 -0
  78. {io4it-3.0.3.2 → io4it-3.0.4}/orangecontrib/IO4IT/widgets/icons/list_aws.png +0 -0
  79. {io4it-3.0.3.2 → io4it-3.0.4}/orangecontrib/IO4IT/widgets/icons/load_md.png +0 -0
  80. {io4it-3.0.3.2 → io4it-3.0.4}/orangecontrib/IO4IT/widgets/icons/mail_loader.png +0 -0
  81. {io4it-3.0.3.2 → io4it-3.0.4}/orangecontrib/IO4IT/widgets/icons/mail_writer.png +0 -0
  82. {io4it-3.0.3.2 → io4it-3.0.4}/orangecontrib/IO4IT/widgets/icons/md.png +0 -0
  83. {io4it-3.0.3.2 → io4it-3.0.4}/orangecontrib/IO4IT/widgets/icons/monitor-email.svg +0 -0
  84. {io4it-3.0.3.2 → io4it-3.0.4}/orangecontrib/IO4IT/widgets/icons/office_normalizer.png +0 -0
  85. {io4it-3.0.3.2 → io4it-3.0.4}/orangecontrib/IO4IT/widgets/icons/owmd2html.svg +0 -0
  86. {io4it-3.0.3.2 → io4it-3.0.4}/orangecontrib/IO4IT/widgets/icons/process_pool_executor.png +0 -0
  87. {io4it-3.0.3.2 → io4it-3.0.4}/orangecontrib/IO4IT/widgets/icons/speech_to_text.png +0 -0
  88. {io4it-3.0.3.2 → io4it-3.0.4}/orangecontrib/IO4IT/widgets/icons/upload.png +0 -0
  89. {io4it-3.0.3.2 → io4it-3.0.4}/orangecontrib/IO4IT/widgets/icons/visualizationer.png +0 -0
  90. {io4it-3.0.3.2 → io4it-3.0.4}/orangecontrib/IO4IT/widgets/icons/websearch.png +0 -0
  91. {io4it-3.0.3.2 → io4it-3.0.4}/orangecontrib/IO4IT/widgets/icons/wordpdf2docx.png +0 -0
  92. {io4it-3.0.3.2 → io4it-3.0.4}/orangecontrib/IO4IT/widgets/icons_dev/__init__.py +0 -0
  93. {io4it-3.0.3.2 → io4it-3.0.4}/orangecontrib/__init__.py +0 -0
  94. {io4it-3.0.3.2 → io4it-3.0.4}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: io4it
3
- Version: 3.0.3.2
3
+ Version: 3.0.4
4
4
  Home-page:
5
5
  Author:
6
6
  Author-email:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: io4it
3
- Version: 3.0.3.2
3
+ Version: 3.0.4
4
4
  Home-page:
5
5
  Author:
6
6
  Author-email:
@@ -23,6 +23,7 @@ orangecontrib/IO4IT/widgets/OWDoclingToMarkdown.py
23
23
  orangecontrib/IO4IT/widgets/OWExportMarkdown.py
24
24
  orangecontrib/IO4IT/widgets/OWExtractTablesDocxToXlsx.py
25
25
  orangecontrib/IO4IT/widgets/OWInboxMailMonitoring.py
26
+ orangecontrib/IO4IT/widgets/OWJsonToDataTable.py
26
27
  orangecontrib/IO4IT/widgets/OWMD2HTML.py
27
28
  orangecontrib/IO4IT/widgets/OWMarkdownLoader.py
28
29
  orangecontrib/IO4IT/widgets/OWMarkdownizer.py
@@ -41,12 +42,13 @@ orangecontrib/IO4IT/widgets/OWwordpdf2docx.py
41
42
  orangecontrib/IO4IT/widgets/__init__.py
42
43
  orangecontrib/IO4IT/widgets/designer/__init__.py
43
44
  orangecontrib/IO4IT/widgets/designer/nogui.ui
45
+ orangecontrib/IO4IT/widgets/designer/ow_extract_tables_docx_to_xlsx.ui
44
46
  orangecontrib/IO4IT/widgets/designer/ow_file_ext_selector.ui
47
+ orangecontrib/IO4IT/widgets/designer/ow_json_to_data_table.ui
45
48
  orangecontrib/IO4IT/widgets/designer/owchatgpt.ui
46
49
  orangecontrib/IO4IT/widgets/designer/owdeepsearch.ui
47
50
  orangecontrib/IO4IT/widgets/designer/owdoclingasr.ui
48
51
  orangecontrib/IO4IT/widgets/designer/owdoclingtomarkdown.ui
49
- orangecontrib/IO4IT/widgets/designer/owdocxtoxlsx.ui
50
52
  orangecontrib/IO4IT/widgets/designer/owexportmarkdown.ui
51
53
  orangecontrib/IO4IT/widgets/designer/owinboxmailmonitoring.ui
52
54
  orangecontrib/IO4IT/widgets/designer/owmailloader.ui
@@ -72,6 +74,7 @@ orangecontrib/IO4IT/widgets/icons/export_md.png
72
74
  orangecontrib/IO4IT/widgets/icons/extract_table.png
73
75
  orangecontrib/IO4IT/widgets/icons/file_extensor.png
74
76
  orangecontrib/IO4IT/widgets/icons/html.png
77
+ orangecontrib/IO4IT/widgets/icons/json-file.png
75
78
  orangecontrib/IO4IT/widgets/icons/list_aws.png
76
79
  orangecontrib/IO4IT/widgets/icons/load_md.png
77
80
  orangecontrib/IO4IT/widgets/icons/mail_loader.png
@@ -1,4 +1,5 @@
1
1
  import os
2
+ import re
2
3
  import json
3
4
  import hashlib
4
5
  import getpass
@@ -11,6 +12,21 @@ else:
11
12
  from orangecontrib.AAIT.utils import MetManagement
12
13
  from orangecontrib.IO4IT.utils import secret_manager
13
14
 
15
+
16
+
17
+ def _normalize_single_quotes_json(s: str) -> str:
18
+ """
19
+ Convertit un pseudo-JSON avec quotes simples
20
+ en JSON valide, de manière contrôlée.
21
+ """
22
+ # ⚠️ On ne touche qu'aux clés et valeurs simples
23
+ # 'key': 'value' → "key": "value"
24
+ s = re.sub(r"'([^']*)'\s*:", r'"\1":', s)
25
+ s = re.sub(r":\s*'([^']*)'", r': "\1"', s)
26
+ return s
27
+
28
+
29
+
14
30
  def ask_secure():
15
31
  while True:
16
32
  resp = input("Secure? (Y/N) : ").strip().lower()
@@ -240,6 +256,25 @@ def enregistrer_config_owa_secure(mail, alias, server, username, password, inter
240
256
 
241
257
 
242
258
 
259
+
260
+ def lire_config_imap4_ssl_dict_sec(chemin_fichier):
261
+ if len(chemin_fichier)<5:
262
+ print(f"❌ Error path not correct", chemin_fichier)
263
+ return None
264
+ if not chemin_fichier.endswith(".sec"):
265
+ chemin_fichier += ".sec"
266
+ try:
267
+ service = "IMAP4_SSL__" + chemin_fichier[:-4]
268
+ sm = secret_manager.SecretManager(service)
269
+ contenu = sm.load_all()
270
+ return contenu
271
+
272
+
273
+ except Exception as e:
274
+ print(f"❌ Erreur lors de la lecture : {e}")
275
+ return None
276
+
277
+
243
278
  # Fonction pour lire le fichier de configuration et déchiffrer le mot de passe
244
279
  def lire_config_imap4_ssl(chemin_fichier):
245
280
  # renvoie une liste =["agent","domain",mdp,"interval_second"]
@@ -251,6 +286,7 @@ def lire_config_imap4_ssl(chemin_fichier):
251
286
  service = "IMAP4_SSL__" + chemin_fichier[:-4]
252
287
  sm = secret_manager.SecretManager(service)
253
288
  contenu = sm.load_all()
289
+ print(contenu)
254
290
  return [
255
291
  contenu["agent"],
256
292
  contenu["domain"],
@@ -284,14 +320,30 @@ def lire_config_imap4_ssl(chemin_fichier):
284
320
  print(f"❌ Erreur lors de la lecture : {e}")
285
321
  return None
286
322
 
287
- def lire_config_oauth2(chemin_fichier):
323
+ def lire_config_oauth2_dict_sec(chemin_fichier, type="MICROSOFT_EXCHANGE_OAUTH2"):
324
+ # renvoie une liste =["agent","domain",mdp,"interval_second"]
325
+ if len(chemin_fichier) < 5:
326
+ print(f"❌ Error path not correct", chemin_fichier)
327
+ return None
328
+ if not chemin_fichier.endswith(".sec"):
329
+ chemin_fichier+=".sec"
330
+ try:
331
+ service = type + "__" + chemin_fichier[:-4]
332
+ sm = secret_manager.SecretManager(service)
333
+ contenu = sm.load_all()
334
+ return contenu
335
+ except Exception as e:
336
+ print(f"❌ Erreur lors de la lecture : {e}")
337
+ return None
338
+
339
+ def lire_config_oauth2(chemin_fichier, type="MICROSOFT_EXCHANGE_OAUTH2"):
288
340
  # renvoie une liste =["agent","domain",mdp,"interval_second"]
289
341
  if len(chemin_fichier) < 5:
290
342
  print(f"❌ Error path not correct", chemin_fichier)
291
343
  return None
292
344
  if chemin_fichier.endswith(".sec"):
293
345
  try:
294
- service = "MICROSOFT_EXCHANGE_OAUTH2__" + chemin_fichier[:-4]
346
+ service = type + "__" + chemin_fichier[:-4]
295
347
  sm = secret_manager.SecretManager(service)
296
348
  contenu = sm.load_all()
297
349
  return [
@@ -305,7 +357,7 @@ def lire_config_oauth2(chemin_fichier):
305
357
  print(f"❌ Erreur lors de la lecture : {e}")
306
358
  return None
307
359
  try:
308
- chemin_fichier = os.path.join(get_keys_dir("MICROSOFT_EXCHANGE_OAUTH2"), chemin_fichier)
360
+ chemin_fichier = os.path.join(get_keys_dir(type), chemin_fichier)
309
361
  # Lecture du fichier JSON
310
362
  with open(chemin_fichier, "r", encoding="utf-8") as f:
311
363
  contenu = json.load(f)
@@ -327,6 +379,21 @@ def lire_config_oauth2(chemin_fichier):
327
379
  print(f"❌ Erreur lors de la lecture : {e}")
328
380
  return None
329
381
 
382
+ def lire_config_owa_dict_sec(chemin_fichier):
383
+ if len(chemin_fichier)<5:
384
+ print(f"❌ Error path not correct", chemin_fichier)
385
+ return None
386
+ if not chemin_fichier.endswith(".sec"):
387
+ chemin_fichier += ".sec"
388
+ try:
389
+ service="MICROSOFT_EXCHANGE_OWA__"+chemin_fichier[:-4]
390
+ sm = secret_manager.SecretManager(service)
391
+ contenu = sm.load_all()
392
+ return contenu
393
+
394
+ except Exception as e:
395
+ print(f"❌ Erreur lors de la lecture : {e}")
396
+ return None
330
397
 
331
398
 
332
399
  def lire_config_owa(chemin_fichier):
@@ -412,15 +479,15 @@ def enregistrer_config_cli_owa():
412
479
  if 0 != enregistrer_config_owa_secure(mail, alias, server, username, mdp, interval):
413
480
  print("erreur!")
414
481
 
415
- def enregistrer_config_oauth2_secure(client_id, client_secret, tenant_id, user_email):
416
- dossier = get_keys_dir("MICROSOFT_EXCHANGE_OAUTH2")
482
+ def enregistrer_config_oauth2_secure(client_id, client_secret, tenant_id, user_email, type="MICROSOFT_EXCHANGE_OAUTH2"):
483
+ dossier = get_keys_dir(type)
417
484
  # Crée le dossier s'il n'existe pas
418
485
  if not os.path.exists(dossier):
419
486
  os.makedirs(dossier)
420
487
 
421
488
  # Nom du fichier (remplace @ par _at_ pour éviter les problèmes)
422
489
  nom_fichier = os.path.join(dossier, f"{user_email.replace('@', '_at_')}.sec")
423
- service_name = f"MICROSOFT_EXCHANGE_OAUTH2__{alias.replace('@', '_at_')}"
490
+ service_name = f"{type}__{user_email.replace('@', '_at_')}"
424
491
  key = get_user_key()
425
492
 
426
493
  contenu = {
@@ -440,8 +507,8 @@ def enregistrer_config_oauth2_secure(client_id, client_secret, tenant_id, user_e
440
507
  return 0
441
508
 
442
509
 
443
- def enregistrer_config_oauth2(client_id, client_secret, tenant_id, user_email):
444
- dossier = get_keys_dir("MICROSOFT_EXCHANGE_OAUTH2")
510
+ def enregistrer_config_oauth2(client_id, client_secret, tenant_id, user_email, type="MICROSOFT_EXCHANGE_OAUTH2"):
511
+ dossier = get_keys_dir(type)
445
512
  # Crée le dossier s'il n'existe pas
446
513
  if not os.path.exists(dossier):
447
514
  os.makedirs(dossier)
@@ -472,27 +539,32 @@ def enregistrer_config_oauth2(client_id, client_secret, tenant_id, user_email):
472
539
 
473
540
  def enregistrer_config_cli_oauth2():
474
541
  print("\n📝 Écriture d’un fichier de configuration OAuth2 :")
542
+ type = input(
543
+ "📄 Nom du service à choisir entre MICROSOFT_EXCHANGE_OAUTH2 et MICROSOFT_EXCHANGE_OAUTH2_MICROSOFT_GRAPH : ").strip()
475
544
  client_id = input("🆔 Client ID : ").strip()
476
545
  client_secret = input("🔑 Client Secret : ").strip()
477
546
  tenant_id = input("🏢 Tenant ID (GUID Azure) : ").strip()
478
547
  user_email = input("📨 Adresse email de l'utilisateur Exchange : ").strip()
479
548
  store = ask_secure()
480
549
  if not store:
481
- if 0 != enregistrer_config_oauth2(client_id, client_secret, tenant_id, user_email):
550
+ if 0 != enregistrer_config_oauth2(client_id, client_secret, tenant_id, user_email, type):
482
551
  print("erreur!")
483
552
  return
484
- if 0 != enregistrer_config_oauth2_secure(client_id, client_secret, tenant_id, user_email):
553
+ if 0 != enregistrer_config_oauth2_secure(client_id, client_secret, tenant_id, user_email, type):
485
554
  print("erreur!")
486
555
 
487
556
 
488
557
 
489
558
 
490
- def lire_config_cli_oauth2():
559
+ def lire_config_cli_oauth2(type=""):
560
+ if type == "":
561
+ type = input("📄 Nom du service à choisir entre MICROSOFT_EXCHANGE_OAUTH2 et MICROSOFT_EXCHANGE_OAUTH2_MICROSOFT_GRAPH : ").strip()
491
562
  chemin_fichier = input("📄 nom fichier json (pas le chemin!) JSON : ").strip()
492
- config = lire_config_oauth2(chemin_fichier)
563
+ print("oeoeo ", chemin_fichier, type)
564
+ config = lire_config_oauth2(chemin_fichier, type)
493
565
  if config == None:
494
566
  print("erreur")
495
- print(config)
567
+
496
568
 
497
569
 
498
570
 
@@ -539,6 +611,59 @@ def enregistrer_config_api(service_name, api_key, description=""):
539
611
  print(f"❌ Erreur d’enregistrement : {e}")
540
612
  return 1
541
613
 
614
+
615
+ def parse_json_and_save_api(config_str: str) -> None:
616
+ raw = config_str.strip()
617
+
618
+ # 1️⃣ Parsing initial
619
+ try:
620
+ data = json.loads(raw)
621
+ except json.JSONDecodeError as e:
622
+ # 2️⃣ Cas très courant : quotes simples
623
+ if "double quotes" in e.msg.lower():
624
+ try:
625
+ normalized = _normalize_single_quotes_json(raw)
626
+ data = json.loads(normalized)
627
+ except json.JSONDecodeError:
628
+ raise ValueError(
629
+ "Invalid JSON: use double quotes "
630
+ '(example: {"service": "xxx"})'
631
+ ) from None
632
+ else:
633
+ raise ValueError(f"Invalid JSON syntax: {e.msg}") from None
634
+
635
+ # 3️⃣ Type attendu
636
+ if not isinstance(data, dict):
637
+ raise TypeError("JSON root must be an object")
638
+
639
+ # 4️⃣ Champs obligatoires
640
+ required_keys = ("service", "api_key")
641
+ missing = [k for k in required_keys if k not in data]
642
+ if missing:
643
+ raise ValueError(f"Missing required key(s): {', '.join(missing)}")
644
+
645
+ # 5️⃣ Validation stricte
646
+ for key in required_keys:
647
+ if not isinstance(data[key], str):
648
+ raise TypeError(f"'{key}' must be a string")
649
+ if not data[key].strip():
650
+ raise ValueError(f"'{key}' must be a non-empty string")
651
+
652
+ # 6️⃣ Champ optionnel
653
+ desc = data.get("description", "")
654
+ if not isinstance(desc, str):
655
+ raise TypeError("'description' must be a string")
656
+
657
+ # 7️⃣ Appel sécurisé
658
+ enregistrer_config_api_secure(
659
+ data["service"],
660
+ data["api_key"],
661
+ desc
662
+ )
663
+
664
+
665
+
666
+
542
667
  # Gestion clés API (HARD dossier aait_store/keys)
543
668
  # Enregistre un fichier JSON {service, api_key_encrypted, description}
544
669
  def enregistrer_config_api_secure(service_name, api_key, description=""):
@@ -435,10 +435,8 @@ def check_new_emails(offusc_conf_agent,type_co, list_agent_email=[]):
435
435
 
436
436
  elif type_co == "MICROSOFT_EXCHANGE_OAUTH2_MICROSOFT_GRAPH":
437
437
  try:
438
- print("🔍 [DEBUG] Début de la vérification des emails avec Microsoft Graph OAuth2")
439
438
  client_id, client_secret, tenant_id, user_email = keys_manager.lire_config_oauth2(
440
- offusc_conf_agent)
441
- print(f"🔍 [DEBUG] Configuration lue - User: {user_email}, Tenant: {tenant_id}")
439
+ offusc_conf_agent, type=type_co)
442
440
 
443
441
  authority = f"https://login.microsoftonline.com/{tenant_id}"
444
442
 
@@ -447,14 +445,12 @@ def check_new_emails(offusc_conf_agent,type_co, list_agent_email=[]):
447
445
  client_credential=client_secret,
448
446
  authority=authority
449
447
  )
450
- print("🔍 [DEBUG] Application MSAL créée")
451
448
 
452
449
  # Essayer d'abord avec les permissions d'application (Client Credentials)
453
450
  token_result = app.acquire_token_for_client(scopes=["https://graph.microsoft.com/.default"])
454
451
  if "access_token" not in token_result:
455
452
  raise Exception("Impossible d'obtenir un token : ", token_result.get("error_description"))
456
453
 
457
- print("🔍 [DEBUG] Token d'accès obtenu avec succès (Client Credentials)")
458
454
  access_token = token_result['access_token']
459
455
  headers = {
460
456
  'Authorization': f'Bearer {access_token}',
@@ -469,40 +465,13 @@ def check_new_emails(offusc_conf_agent,type_co, list_agent_email=[]):
469
465
  '$select': 'id,subject,sender,toRecipients,ccRecipients,receivedDateTime,body,hasAttachments'
470
466
  }
471
467
 
472
- print(f"🔍 [DEBUG] Requête Graph API: {graph_url}")
473
- print(f"🔍 [DEBUG] Paramètres: {params}")
474
468
 
475
469
  try:
476
470
  response = requests.get(graph_url, headers=headers, params=params)
477
471
  response.raise_for_status()
478
472
  emails_data = response.json()
479
- print(
480
- f"🔍 [DEBUG] Réponse Graph API reçue - Nombre d'emails non lus: {len(emails_data.get('value', []))}")
481
473
  except requests.exceptions.HTTPError as e:
482
474
  if response.status_code == 403:
483
- print("🔍 [DEBUG] Erreur 403 - Permissions insuffisantes")
484
- print("🔍 [DEBUG] ==============================================")
485
- print("🔍 [DEBUG] ACTION REQUISE - Contactez votre IT Admin:")
486
- print("🔍 [DEBUG] ==============================================")
487
- print("🔍 [DEBUG] Votre application Azure AD manque des permissions.")
488
- print("🔍 [DEBUG] App: IA-ODATAMINING")
489
- print("🔍 [DEBUG] ID: 30ae55f5-f41e-4d44-afcc-082e4b803ba1")
490
- print("🔍 [DEBUG] Tenant: 9988c2b8-3feb-4426-aeb7-b8d695bcd025")
491
- print("🔍 [DEBUG] ")
492
- print("🔍 [DEBUG] Permissions manquantes (Application type):")
493
- print("🔍 [DEBUG] - Mail.Read - pour lire les emails")
494
- print("🔍 [DEBUG] - Mail.Send - pour envoyer des emails")
495
- print("🔍 [DEBUG] ")
496
- print("🔍 [DEBUG] INFORMATIONS POUR VOTRE IT ADMIN:")
497
- print("🔍 [DEBUG] 1. Azure Portal → Azure AD → App registrations")
498
- print("🔍 [DEBUG] 2. Trouver l'app: IA-ODATAMINING")
499
- print("🔍 [DEBUG] 3. API permissions → Add permission → Microsoft Graph")
500
- print("🔍 [DEBUG] 4. Application permissions → Mail.Read + Mail.Send")
501
- print("🔍 [DEBUG] 5. Grant admin consent")
502
- print("🔍 [DEBUG] ")
503
- print("🔍 [DEBUG] URL de consentement admin:")
504
- print(f"🔍 [DEBUG] https://login.microsoftonline.com/{tenant_id}/adminconsent?client_id={client_id}")
505
- print("🔍 [DEBUG] ==============================================")
506
475
  raise Exception(
507
476
  "❌ Permissions insuffisantes. Contactez votre IT Admin pour ajouter Mail.Read et Mail.Send permissions à l'application IA-ODATAMINING.")
508
477
  else:
@@ -511,7 +480,6 @@ def check_new_emails(offusc_conf_agent,type_co, list_agent_email=[]):
511
480
  print("Aucun nouveau mail.")
512
481
  else:
513
482
  for i, email_item in enumerate(emails_data['value']):
514
- print(f"🔍 [DEBUG] Traitement de l'email {i + 1}/{len(emails_data['value'])}")
515
483
  if list_agent_email:
516
484
  white_list, black_list = keys_manager.lire_list_email(list_agent_email)
517
485
  print(f"🔍 [DEBUG] Listes chargées - White: {len(white_list)}, Black: {len(black_list)}")
@@ -521,60 +489,48 @@ def check_new_emails(offusc_conf_agent,type_co, list_agent_email=[]):
521
489
  time.sleep(1.5)
522
490
  output_lines = []
523
491
 
524
- email_id = email_item['id']
525
- from_ = email_item['sender']['emailAddress']['address']
526
- subject = email_item.get('subject', '(Sans sujet)')
527
- to_emails = [rec['emailAddress']['address'] for rec in email_item.get('toRecipients', [])]
528
- cc_emails = [rec['emailAddress']['address'] for rec in email_item.get('ccRecipients', [])]
492
+ email_id = email_item["id"]
529
493
 
530
- print(f"🔍 [DEBUG] Email ID: {email_id}")
531
- print(f"🔍 [DEBUG] De: {from_}, Sujet: {subject}")
532
- print(f"🔍 [DEBUG] Destinataires: {to_emails}, CC: {cc_emails}")
494
+ from_ = (email_item.get("from") or {}).get("emailAddress", {}).get("address")
495
+ if not from_:
496
+ from_ = (email_item.get("sender") or {}).get("emailAddress", {}).get("address")
533
497
 
534
- # Marquer comme lu
535
- mark_read_url = f"https://graph.microsoft.com/v1.0/users/{user_email}/messages/{email_id}"
536
- patch_data = {'isRead': True}
537
- print(f"🔍 [DEBUG] Marquage comme lu: {mark_read_url}")
538
- requests.patch(mark_read_url, headers=headers, json=patch_data)
498
+ subject = email_item.get("subject", "(Sans sujet)")
499
+ to_emails = [rec["emailAddress"]["address"] for rec in email_item.get("toRecipients", [])]
500
+ cc_emails = [rec["emailAddress"]["address"] for rec in email_item.get("ccRecipients", [])]
539
501
 
502
+ mark_read_url = f"https://graph.microsoft.com/v1.0/users/{user_email}/messages/{email_id}"
503
+ requests.patch(mark_read_url, headers=headers, json={"isRead": True})
540
504
  # Récupérer le corps du message
541
505
  body = ""
542
506
  if email_item.get('body'):
543
507
  body_content = email_item['body'].get('content', '')
544
508
  if email_item['body'].get('contentType') == 'html':
545
- print("🔍 [DEBUG] Corps HTML détecté, nettoyage en cours")
546
509
  soup = BeautifulSoup(body_content, "html.parser")
547
510
  for block in soup.find_all(["footer", "style", "script"]):
548
511
  block.decompose()
549
512
  body = soup.get_text(separator="\n", strip=True)
550
513
  else:
551
514
  body = body_content.strip()
552
- print(f"🔍 [DEBUG] Corps du message extrait (longueur: {len(body)})")
553
515
 
554
516
  folder = mail_in_folder(user_email, "in")
555
517
  if folder is None:
556
518
  print("Erreur dans le folder de mail")
557
519
  continue
558
- print(f"🔍 [DEBUG] Dossier de mail: {folder}")
559
520
 
560
521
  ignored_pj = ""
561
522
  # Traiter les pièces jointes si elles existent
562
523
  if email_item.get('hasAttachments'):
563
- print("🔍 [DEBUG] Pièces jointes détectées, récupération en cours")
564
524
  attachments_url = f"https://graph.microsoft.com/v1.0/users/{user_email}/messages/{email_id}/attachments"
565
525
  att_response = requests.get(attachments_url, headers=headers)
566
526
  att_response.raise_for_status()
567
527
  attachments_data = att_response.json()
568
-
569
- print(f"🔍 [DEBUG] {len(attachments_data.get('value', []))} pièce(s) jointe(s) trouvée(s)")
570
528
  for attachment in attachments_data.get('value', []):
571
529
  attachment_name = attachment.get('name', '')
572
- print(f"🔍 [DEBUG] Pièce jointe: {attachment_name}")
573
530
  if len(attachment_name) < 5:
574
531
  continue
575
532
  if not (attachment_name.endswith(".pdf") or attachment_name.endswith(".docx")):
576
533
  ignored_pj += (";" if ignored_pj else "") + attachment_name
577
- print(f"🔍 [DEBUG] Pièce jointe ignorée: {attachment_name}")
578
534
  continue
579
535
 
580
536
  folder_pj = os.path.join(folder, "pj")
@@ -585,7 +541,6 @@ def check_new_emails(offusc_conf_agent,type_co, list_agent_email=[]):
585
541
  content_bytes = base64.b64decode(attachment.get('contentBytes', ''))
586
542
  with open(filepath, "wb") as f:
587
543
  f.write(content_bytes)
588
- print(f"🔍 [DEBUG] Pièce jointe sauvegardée: {filepath}")
589
544
 
590
545
  # Format sortie
591
546
  output_lines = [
@@ -613,14 +568,10 @@ def check_new_emails(offusc_conf_agent,type_co, list_agent_email=[]):
613
568
 
614
569
  mail_txt_path = os.path.join(folder, "mail.txt")
615
570
  mail_ok_path = os.path.join(folder, "mail.ok")
616
- print(f"🔍 [DEBUG] Sauvegarde du mail: {mail_txt_path}")
617
571
  with open(mail_txt_path, "w", encoding="utf-8") as f:
618
572
  f.write("\n".join(output_lines))
619
573
  with open(mail_ok_path, "w") as f:
620
574
  pass
621
- print("🔍 [DEBUG] Fichiers créés: mail.txt et mail.ok")
622
-
623
- print("🔍 [DEBUG] Vérification des emails terminée")
624
575
 
625
576
  except Exception as e:
626
577
  print(f"Erreur Graph API lors du traitement du mail : {e}")
@@ -858,17 +809,14 @@ def check_send_new_emails(offusc_conf_agent,type_co):
858
809
  print(f"❌ Erreur lors de l'envoi avec OAuth2 : {e}")
859
810
  elif type_co == "MICROSOFT_EXCHANGE_OAUTH2_MICROSOFT_GRAPH":
860
811
  try:
861
- print("📤 [DEBUG] Début de la vérification des emails à envoyer avec Microsoft Graph OAuth2")
862
812
  client_id, client_secret, tenant_id, user_email = keys_manager.lire_config_oauth2(
863
813
  offusc_conf_agent)
864
- print(f"📤 [DEBUG] Configuration lue - User: {user_email}, Tenant: {tenant_id}")
865
814
 
866
815
  # Déduire l'alias pour le nom du dossier
867
816
  agent = user_email
868
817
  alias = user_email # tu peux adapter si tu veux un alias distinct plus tard
869
818
 
870
819
  chemin_dossier_in, chemin_dossier_out = mail_in_folder(agent, "out")
871
- print(f"📤 [DEBUG] Dossiers - In: {chemin_dossier_in}, Out: {chemin_dossier_out}")
872
820
 
873
821
  authority = f"https://login.microsoftonline.com/{tenant_id}"
874
822
 
@@ -878,14 +826,12 @@ def check_send_new_emails(offusc_conf_agent,type_co):
878
826
  client_credential=client_secret,
879
827
  authority=authority
880
828
  )
881
- print("📤 [DEBUG] Application MSAL créée")
882
829
 
883
830
  token_result = app.acquire_token_for_client(scopes=["https://graph.microsoft.com/.default"])
884
831
 
885
832
  if "access_token" not in token_result:
886
833
  raise Exception("Impossible d'obtenir un token : ", token_result.get("error_description"))
887
834
 
888
- print("📤 [DEBUG] Token d'accès obtenu avec succès (Client Credentials)")
889
835
  access_token = token_result['access_token']
890
836
  headers = {
891
837
  'Authorization': f'Bearer {access_token}',
@@ -893,26 +839,16 @@ def check_send_new_emails(offusc_conf_agent,type_co):
893
839
  }
894
840
 
895
841
  if os.path.exists(chemin_dossier_out) and os.path.isdir(chemin_dossier_out):
896
- print("📤 [DEBUG] Dossier de sortie trouvé, lecture du contenu")
897
842
  contenus = os.listdir(chemin_dossier_out)
898
- print(f"📤 [DEBUG] Contenu du dossier: {contenus}")
899
843
  if contenus:
900
844
  for contenu in contenus:
901
845
  mail_ok_path = os.path.join(chemin_dossier_out, contenu, "mail.ok")
902
- print(f"📤 [DEBUG] Vérification du fichier mail.ok: {mail_ok_path}")
903
846
  if os.path.exists(mail_ok_path):
904
847
  chemin = os.path.join(chemin_dossier_out, contenu, "mail.txt")
905
- print(f"📤 [DEBUG] Lecture du message depuis: {chemin}")
906
848
  infos = lire_message(chemin)
907
- print(f"📤 [DEBUG] Informations du message lues: {list(infos.keys())}")
908
849
 
909
850
  cles_requises = ["eme", "des", "cop", "pri", "tit", "txt"]
910
851
  if all(cle in infos for cle in cles_requises):
911
- print("📤 [DEBUG] Toutes les clés requises sont présentes")
912
- print(f"📤 [DEBUG] Envoi vers: {infos['eme']}")
913
- print(f"📤 [DEBUG] Sujet: {infos['tit']}")
914
- print(f"📤 [DEBUG] Contenu (longueur): {len(infos['txt'])}")
915
-
916
852
  # Construire le message pour Microsoft Graph
917
853
  message_data = {
918
854
  "message": {
@@ -934,39 +870,19 @@ def check_send_new_emails(offusc_conf_agent,type_co):
934
870
 
935
871
  # Envoyer l'email via Microsoft Graph
936
872
  send_url = f"https://graph.microsoft.com/v1.0/users/{user_email}/sendMail"
937
- print(f"📤 [DEBUG] Envoi via Graph API: {send_url}")
938
873
  try:
939
874
  response = requests.post(send_url, headers=headers, json=message_data)
940
875
  response.raise_for_status()
941
- print("📤 [DEBUG] Email envoyé avec succès")
942
876
  except requests.exceptions.HTTPError as e:
943
877
  if response.status_code == 403:
944
- print("📤 [DEBUG] Erreur 403 - Permissions insuffisantes pour l'envoi")
945
- print("📤 [DEBUG] ==============================================")
946
- print("📤 [DEBUG] ACTION REQUISE - Contactez votre IT Admin:")
947
- print("📤 [DEBUG] ==============================================")
948
- print("📤 [DEBUG] Permission manquante: Mail.Send (Application)")
949
- print("📤 [DEBUG] App: IA-ODATAMINING")
950
- print("📤 [DEBUG] ID: 30ae55f5-f41e-4d44-afcc-082e4b803ba1")
951
- print("📤 [DEBUG] Tenant: 9988c2b8-3feb-4426-aeb7-b8d695bcd025")
952
- print("📤 [DEBUG] ")
953
- print("📤 [DEBUG] INFORMATIONS POUR VOTRE IT ADMIN:")
954
- print("📤 [DEBUG] 1. Azure Portal → Azure AD → App registrations")
955
- print("📤 [DEBUG] 2. Trouver l'app: IA-ODATAMINING")
956
- print("📤 [DEBUG] 3. API permissions → Add permission → Microsoft Graph")
957
- print("📤 [DEBUG] 4. Application permissions → Mail.Send")
958
- print("📤 [DEBUG] 5. Grant admin consent")
959
- print("📤 [DEBUG] ==============================================")
960
878
  raise Exception(
961
879
  "❌ Permission Mail.Send manquante. Contactez votre IT Admin pour ajouter cette permission à l'application IA-ODATAMINING.")
962
880
  else:
963
881
  raise e
964
882
 
965
883
  time.sleep(1)
966
- print("📤 [DEBUG] Nettoyage des dossiers")
967
884
  MetManagement.reset_folder(os.path.join(chemin_dossier_in, contenu), recreate=False)
968
885
  MetManagement.reset_folder(os.path.join(chemin_dossier_out, contenu), recreate=False)
969
- print("📤 [DEBUG] Dossiers nettoyés")
970
886
  else:
971
887
  print("\n\n Il manque des clefs dans le contenu du mail")
972
888
  print(
@@ -1005,7 +921,7 @@ def list_conf_files(type_co):
1005
921
 
1006
922
 
1007
923
  if __name__ == "__main__":
1008
- type_co="IMAP4_SSL"
924
+ type_co="MICROSOFT_EXCHANGE_OAUTH2_MICROSOFT_GRAPH"
1009
925
  list_agent_email = []
1010
926
  offusc_conf_agents = list_conf_files(type_co)
1011
927
  while True:
@@ -183,18 +183,21 @@ class OWExportMarkdown(widget.OWWidget):
183
183
  )
184
184
  else:
185
185
  raise Exception("Word non détecté")
186
-
187
- except Exception:
186
+ # PDF (docx -> pdf) avec chemin de sortie exact
188
187
  try:
189
- pypandoc.convert_file(tmp_md, to="pdf", outputfile=pdf_out)
188
+ convert(docx_out, pdf_out)
190
189
  except Exception:
191
- self.error(f"Échec conversion PDF pour la ligne {i + 1}.")
192
- pdf_out = ""
190
+ # fallback pandoc->pdf (si LaTeX dispo)
191
+ try:
192
+ pypandoc.convert_file(tmp_md, to="pdf", outputfile=pdf_out)
193
+ except Exception:
194
+ self.error(f"Échec conversion PDF pour la ligne {i + 1}.")
195
+ pdf_out = ""
193
196
  finally:
194
- try:
195
- os.remove(tmp_md)
196
- except Exception:
197
- pass
197
+ try:
198
+ os.remove(tmp_md)
199
+ except Exception:
200
+ pass
198
201
 
199
202
  pdf_paths.append(pdf_out if os.path.isfile(pdf_out) else "")
200
203
  docx_paths.append(docx_out if os.path.isfile(docx_out) else "")