IO4IT 2.1.0__tar.gz → 2.1.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.
- io4it-2.1.0.4/PKG-INFO +33 -0
- io4it-2.1.0.4/io4it.egg-info/PKG-INFO +33 -0
- {io4it-2.1.0 → io4it-2.1.0.4}/io4it.egg-info/SOURCES.txt +2 -0
- {io4it-2.1.0 → io4it-2.1.0.4}/orangecontrib/IO4IT/utils/mail.py +33 -13
- {io4it-2.1.0 → io4it-2.1.0.4}/orangecontrib/IO4IT/utils/offuscation_basique.py +123 -16
- io4it-2.1.0.4/orangecontrib/IO4IT/utils/pool_exec_utils.py +33 -0
- io4it-2.1.0.4/orangecontrib/IO4IT/utils/utils_md.py +168 -0
- {io4it-2.1.0 → io4it-2.1.0.4}/orangecontrib/IO4IT/widgets/OWExportMarkdown.py +2 -2
- io4it-2.1.0.4/orangecontrib/IO4IT/widgets/OWMarkdownizer.py +652 -0
- {io4it-2.1.0 → io4it-2.1.0.4}/orangecontrib/IO4IT/widgets/OWS3Uploader.py +1 -1
- {io4it-2.1.0 → io4it-2.1.0.4}/orangecontrib/IO4IT/widgets/OWmailLoader.py +3 -2
- io4it-2.1.0.4/orangecontrib/IO4IT/widgets/designer/ow_file_ext_selector.ui +82 -0
- io4it-2.1.0.4/orangecontrib/IO4IT/widgets/designer/owchatgpt.ui +168 -0
- io4it-2.1.0.4/orangecontrib/IO4IT/widgets/designer/owdeepsearch.ui +87 -0
- io4it-2.1.0.4/orangecontrib/IO4IT/widgets/designer/owexportmarkdown.ui +72 -0
- io4it-2.1.0.4/orangecontrib/IO4IT/widgets/designer/owmailloader.ui +149 -0
- io4it-2.1.0.4/orangecontrib/IO4IT/widgets/designer/owmailsender.ui +149 -0
- io4it-2.1.0.4/orangecontrib/IO4IT/widgets/designer/owmarkdownizer.ui +103 -0
- io4it-2.1.0.4/orangecontrib/IO4IT/widgets/designer/owspeechtotext.ui +130 -0
- io4it-2.1.0.4/orangecontrib/IO4IT/widgets/designer/wordpdf2docx.ui +70 -0
- {io4it-2.1.0 → io4it-2.1.0.4}/setup.py +1 -1
- io4it-2.1.0/PKG-INFO +0 -7
- io4it-2.1.0/io4it.egg-info/PKG-INFO +0 -7
- io4it-2.1.0/orangecontrib/IO4IT/widgets/OWMarkdownizer.py +0 -563
- io4it-2.1.0/orangecontrib/IO4IT/widgets/designer/ow_file_ext_selector.ui +0 -69
- io4it-2.1.0/orangecontrib/IO4IT/widgets/designer/owchatgpt.ui +0 -155
- io4it-2.1.0/orangecontrib/IO4IT/widgets/designer/owdeepsearch.ui +0 -74
- io4it-2.1.0/orangecontrib/IO4IT/widgets/designer/owexportmarkdown.ui +0 -59
- io4it-2.1.0/orangecontrib/IO4IT/widgets/designer/owmailloader.ui +0 -32
- io4it-2.1.0/orangecontrib/IO4IT/widgets/designer/owmailsender.ui +0 -32
- io4it-2.1.0/orangecontrib/IO4IT/widgets/designer/owmarkdownizer.ui +0 -101
- io4it-2.1.0/orangecontrib/IO4IT/widgets/designer/owspeechtotext.ui +0 -104
- io4it-2.1.0/orangecontrib/IO4IT/widgets/designer/wordpdf2docx.ui +0 -57
- {io4it-2.1.0 → io4it-2.1.0.4}/io4it.egg-info/dependency_links.txt +0 -0
- {io4it-2.1.0 → io4it-2.1.0.4}/io4it.egg-info/entry_points.txt +0 -0
- {io4it-2.1.0 → io4it-2.1.0.4}/io4it.egg-info/namespace_packages.txt +0 -0
- {io4it-2.1.0 → io4it-2.1.0.4}/io4it.egg-info/requires.txt +0 -0
- {io4it-2.1.0 → io4it-2.1.0.4}/io4it.egg-info/top_level.txt +0 -0
- {io4it-2.1.0 → io4it-2.1.0.4}/orangecontrib/IO4IT/__init__.py +0 -0
- {io4it-2.1.0 → io4it-2.1.0.4}/orangecontrib/IO4IT/ocr_function/__init__.py +0 -0
- {io4it-2.1.0 → io4it-2.1.0.4}/orangecontrib/IO4IT/ocr_function/word_converter.py +0 -0
- {io4it-2.1.0 → io4it-2.1.0.4}/orangecontrib/IO4IT/utils/__init__.py +0 -0
- {io4it-2.1.0 → io4it-2.1.0.4}/orangecontrib/IO4IT/widgets/OWChatGpt.py +0 -0
- {io4it-2.1.0 → io4it-2.1.0.4}/orangecontrib/IO4IT/widgets/OWDeep_Search.py +0 -0
- {io4it-2.1.0 → io4it-2.1.0.4}/orangecontrib/IO4IT/widgets/OWS3downloader.py +0 -0
- {io4it-2.1.0 → io4it-2.1.0.4}/orangecontrib/IO4IT/widgets/OWS3list.py +0 -0
- {io4it-2.1.0 → io4it-2.1.0.4}/orangecontrib/IO4IT/widgets/OWSpeechToText.py +0 -0
- {io4it-2.1.0 → io4it-2.1.0.4}/orangecontrib/IO4IT/widgets/OWmailSender.py +0 -0
- {io4it-2.1.0 → io4it-2.1.0.4}/orangecontrib/IO4IT/widgets/OWwordpdf2docx.py +0 -0
- {io4it-2.1.0 → io4it-2.1.0.4}/orangecontrib/IO4IT/widgets/__init__.py +0 -0
- {io4it-2.1.0 → io4it-2.1.0.4}/orangecontrib/IO4IT/widgets/designer/__init__.py +0 -0
- {io4it-2.1.0 → io4it-2.1.0.4}/orangecontrib/IO4IT/widgets/designer/chart.html +0 -0
- {io4it-2.1.0 → io4it-2.1.0.4}/orangecontrib/IO4IT/widgets/designer/nogui.ui +0 -0
- {io4it-2.1.0 → io4it-2.1.0.4}/orangecontrib/IO4IT/widgets/designer/owvisualizationer.ui +0 -0
- {io4it-2.1.0 → io4it-2.1.0.4}/orangecontrib/IO4IT/widgets/icons/__init__.py +0 -0
- {io4it-2.1.0 → io4it-2.1.0.4}/orangecontrib/IO4IT/widgets/icons/chatgpt.png +0 -0
- {io4it-2.1.0 → io4it-2.1.0.4}/orangecontrib/IO4IT/widgets/icons/deepsearch.svg +0 -0
- {io4it-2.1.0 → io4it-2.1.0.4}/orangecontrib/IO4IT/widgets/icons/download.png +0 -0
- {io4it-2.1.0 → io4it-2.1.0.4}/orangecontrib/IO4IT/widgets/icons/export_md.png +0 -0
- {io4it-2.1.0 → io4it-2.1.0.4}/orangecontrib/IO4IT/widgets/icons/file_extensor.png +0 -0
- {io4it-2.1.0 → io4it-2.1.0.4}/orangecontrib/IO4IT/widgets/icons/list_aws.png +0 -0
- {io4it-2.1.0 → io4it-2.1.0.4}/orangecontrib/IO4IT/widgets/icons/mail_loader.png +0 -0
- {io4it-2.1.0 → io4it-2.1.0.4}/orangecontrib/IO4IT/widgets/icons/mail_writer.png +0 -0
- {io4it-2.1.0 → io4it-2.1.0.4}/orangecontrib/IO4IT/widgets/icons/md.png +0 -0
- {io4it-2.1.0 → io4it-2.1.0.4}/orangecontrib/IO4IT/widgets/icons/speech_to_text.png +0 -0
- {io4it-2.1.0 → io4it-2.1.0.4}/orangecontrib/IO4IT/widgets/icons/upload.png +0 -0
- {io4it-2.1.0 → io4it-2.1.0.4}/orangecontrib/IO4IT/widgets/icons/visualizationer.png +0 -0
- {io4it-2.1.0 → io4it-2.1.0.4}/orangecontrib/IO4IT/widgets/icons/wordpdf2docx.png +0 -0
- {io4it-2.1.0 → io4it-2.1.0.4}/orangecontrib/IO4IT/widgets/icons_dev/__init__.py +0 -0
- {io4it-2.1.0 → io4it-2.1.0.4}/orangecontrib/__init__.py +0 -0
- {io4it-2.1.0 → io4it-2.1.0.4}/setup.cfg +0 -0
io4it-2.1.0.4/PKG-INFO
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: io4it
|
|
3
|
+
Version: 2.1.0.4
|
|
4
|
+
Summary: UNKNOWN
|
|
5
|
+
Home-page:
|
|
6
|
+
Author:
|
|
7
|
+
Author-email:
|
|
8
|
+
License: UNKNOWN
|
|
9
|
+
Keywords: orange3 add-on
|
|
10
|
+
Platform: UNKNOWN
|
|
11
|
+
Requires-Dist: pylatexenc
|
|
12
|
+
Requires-Dist: docopt
|
|
13
|
+
Requires-Dist: boto3
|
|
14
|
+
Requires-Dist: opencv-python-headless==4.6.0.66
|
|
15
|
+
Requires-Dist: docling==2.30.0
|
|
16
|
+
Requires-Dist: docling-core==2.26.3
|
|
17
|
+
Requires-Dist: speechbrain
|
|
18
|
+
Requires-Dist: whisper
|
|
19
|
+
Requires-Dist: whisper-openai
|
|
20
|
+
Requires-Dist: pyannote.audio
|
|
21
|
+
Requires-Dist: pyannote-core
|
|
22
|
+
Requires-Dist: pypandoc
|
|
23
|
+
Requires-Dist: pypandoc-binary
|
|
24
|
+
Requires-Dist: scikit-learn
|
|
25
|
+
Requires-Dist: openai
|
|
26
|
+
Requires-Dist: pip-system-certs==5.0
|
|
27
|
+
Requires-Dist: docx2pdf
|
|
28
|
+
Requires-Dist: doc2docx
|
|
29
|
+
Requires-Dist: msal
|
|
30
|
+
Requires-Dist: exchangelib
|
|
31
|
+
Requires-Dist: CATEGORIT
|
|
32
|
+
|
|
33
|
+
UNKNOWN
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: io4it
|
|
3
|
+
Version: 2.1.0.4
|
|
4
|
+
Summary: UNKNOWN
|
|
5
|
+
Home-page:
|
|
6
|
+
Author:
|
|
7
|
+
Author-email:
|
|
8
|
+
License: UNKNOWN
|
|
9
|
+
Keywords: orange3 add-on
|
|
10
|
+
Platform: UNKNOWN
|
|
11
|
+
Requires-Dist: pylatexenc
|
|
12
|
+
Requires-Dist: docopt
|
|
13
|
+
Requires-Dist: boto3
|
|
14
|
+
Requires-Dist: opencv-python-headless==4.6.0.66
|
|
15
|
+
Requires-Dist: docling==2.30.0
|
|
16
|
+
Requires-Dist: docling-core==2.26.3
|
|
17
|
+
Requires-Dist: speechbrain
|
|
18
|
+
Requires-Dist: whisper
|
|
19
|
+
Requires-Dist: whisper-openai
|
|
20
|
+
Requires-Dist: pyannote.audio
|
|
21
|
+
Requires-Dist: pyannote-core
|
|
22
|
+
Requires-Dist: pypandoc
|
|
23
|
+
Requires-Dist: pypandoc-binary
|
|
24
|
+
Requires-Dist: scikit-learn
|
|
25
|
+
Requires-Dist: openai
|
|
26
|
+
Requires-Dist: pip-system-certs==5.0
|
|
27
|
+
Requires-Dist: docx2pdf
|
|
28
|
+
Requires-Dist: doc2docx
|
|
29
|
+
Requires-Dist: msal
|
|
30
|
+
Requires-Dist: exchangelib
|
|
31
|
+
Requires-Dist: CATEGORIT
|
|
32
|
+
|
|
33
|
+
UNKNOWN
|
|
@@ -13,6 +13,8 @@ orangecontrib/IO4IT/ocr_function/word_converter.py
|
|
|
13
13
|
orangecontrib/IO4IT/utils/__init__.py
|
|
14
14
|
orangecontrib/IO4IT/utils/mail.py
|
|
15
15
|
orangecontrib/IO4IT/utils/offuscation_basique.py
|
|
16
|
+
orangecontrib/IO4IT/utils/pool_exec_utils.py
|
|
17
|
+
orangecontrib/IO4IT/utils/utils_md.py
|
|
16
18
|
orangecontrib/IO4IT/widgets/OWChatGpt.py
|
|
17
19
|
orangecontrib/IO4IT/widgets/OWDeep_Search.py
|
|
18
20
|
orangecontrib/IO4IT/widgets/OWExportMarkdown.py
|
|
@@ -4,19 +4,21 @@ import email
|
|
|
4
4
|
from email.header import decode_header
|
|
5
5
|
import time
|
|
6
6
|
import os
|
|
7
|
-
from bs4 import BeautifulSoup
|
|
8
7
|
from email import policy
|
|
9
8
|
from email.message import EmailMessage
|
|
10
9
|
import mimetypes
|
|
11
10
|
|
|
12
|
-
from exchangelib import Credentials,
|
|
11
|
+
from exchangelib import Credentials, HTMLBody, Message, Mailbox
|
|
13
12
|
from exchangelib.protocol import BaseProtocol, NoVerifyHTTPAdapter
|
|
14
13
|
from exchangelib import OAuth2Credentials, Identity, Configuration, Account, DELEGATE
|
|
15
|
-
|
|
14
|
+
|
|
16
15
|
|
|
17
16
|
from bs4 import BeautifulSoup
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
##### please uncomment this line to ignore certificate checking (owa)
|
|
20
|
+
#BaseProtocol.HTTP_ADAPTER_CLS=NoVerifyHTTPAdapter
|
|
21
|
+
|
|
20
22
|
from msal import ConfidentialClientApplication
|
|
21
23
|
from oauthlib.oauth2 import OAuth2Token
|
|
22
24
|
|
|
@@ -185,6 +187,8 @@ def check_new_emails(offusc_conf_agent,type_co, list_agent_email=[]):
|
|
|
185
187
|
with open(folder + "/" + "mail.ok", "w") as f:
|
|
186
188
|
f.close()
|
|
187
189
|
|
|
190
|
+
|
|
191
|
+
|
|
188
192
|
imap.logout()
|
|
189
193
|
except Exception as e:
|
|
190
194
|
print(f"Erreur lors de la vérification des mails : {e}")
|
|
@@ -199,7 +203,7 @@ def check_new_emails(offusc_conf_agent,type_co, list_agent_email=[]):
|
|
|
199
203
|
)
|
|
200
204
|
|
|
201
205
|
account = Account(
|
|
202
|
-
primary_smtp_address=
|
|
206
|
+
primary_smtp_address=alias,
|
|
203
207
|
credentials=credentials,
|
|
204
208
|
config=config,
|
|
205
209
|
autodiscover=False,
|
|
@@ -301,6 +305,7 @@ def check_new_emails(offusc_conf_agent,type_co, list_agent_email=[]):
|
|
|
301
305
|
f.write("\n".join(output_lines))
|
|
302
306
|
with open(os.path.join(folder, "mail.ok"), "w") as f:
|
|
303
307
|
pass
|
|
308
|
+
|
|
304
309
|
except Exception as e:
|
|
305
310
|
print(f"Erreur lors du traitement du mail : {e}")
|
|
306
311
|
elif type_co == "MICROSOFT_EXCHANGE_OAUTH2":
|
|
@@ -427,6 +432,7 @@ def check_new_emails(offusc_conf_agent,type_co, list_agent_email=[]):
|
|
|
427
432
|
with open(os.path.join(folder, "mail.ok"), "w") as f:
|
|
428
433
|
pass
|
|
429
434
|
|
|
435
|
+
|
|
430
436
|
except Exception as e:
|
|
431
437
|
print(f"Erreur OAuth2 lors du traitement du mail : {e}")
|
|
432
438
|
|
|
@@ -662,13 +668,27 @@ def check_send_new_emails(offusc_conf_agent,type_co):
|
|
|
662
668
|
else:
|
|
663
669
|
print("type de co non valide")
|
|
664
670
|
|
|
671
|
+
def list_conf_files(type_co):
|
|
672
|
+
conf_files = []
|
|
673
|
+
if type_co is None or type_co == "":
|
|
674
|
+
return conf_files
|
|
675
|
+
dossier = MetManagement.get_secret_content_dir()
|
|
676
|
+
dossier = dossier + type_co
|
|
677
|
+
files = os.listdir(dossier)
|
|
678
|
+
for file in files:
|
|
679
|
+
if file.lower().endswith(".json"):
|
|
680
|
+
conf_files.append(file)
|
|
681
|
+
return conf_files
|
|
682
|
+
|
|
683
|
+
|
|
684
|
+
|
|
665
685
|
if __name__ == "__main__":
|
|
666
|
-
|
|
667
|
-
#type_co="IMAP4_SSL"
|
|
668
|
-
type_co="MICROSOFT_EXCHANGE_OAUTH2"
|
|
686
|
+
type_co="IMAP4_SSL"
|
|
669
687
|
list_agent_email = []
|
|
688
|
+
offusc_conf_agents = list_conf_files(type_co)
|
|
670
689
|
while True:
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
690
|
+
for offusc_conf_agent in offusc_conf_agents:
|
|
691
|
+
check_new_emails(offusc_conf_agent,type_co, list_agent_email)
|
|
692
|
+
time.sleep(1)
|
|
693
|
+
check_send_new_emails(offusc_conf_agent,type_co)
|
|
694
|
+
time.sleep(1)
|
|
@@ -2,6 +2,9 @@ import os
|
|
|
2
2
|
import json
|
|
3
3
|
import hashlib
|
|
4
4
|
import getpass
|
|
5
|
+
from cryptography.fernet import Fernet
|
|
6
|
+
import base64
|
|
7
|
+
|
|
5
8
|
|
|
6
9
|
if "site-packages/Orange/widgets" in os.path.dirname(os.path.abspath(__file__)).replace("\\", "/"):
|
|
7
10
|
from Orange.widgets.orangecontrib.AAIT.utils import MetManagement
|
|
@@ -47,12 +50,26 @@ def get_keys_dir(type_key: str = "MICROSOFT_EXCHANGE_OAUTH2") -> str:
|
|
|
47
50
|
if os.path.basename(base) != "keys":
|
|
48
51
|
base = os.path.join(base, "keys")
|
|
49
52
|
dossier = os.path.normpath(os.path.join(base, type_key))
|
|
50
|
-
print("dossier", dossier)
|
|
51
53
|
os.makedirs(dossier, exist_ok=True)
|
|
52
54
|
return dossier
|
|
53
55
|
except Exception as e:
|
|
54
56
|
raise RuntimeError(f"Erreur création/récupération dossier : {e}")
|
|
55
57
|
|
|
58
|
+
def get_fernet_key() -> bytes:
|
|
59
|
+
"""
|
|
60
|
+
Dérive une clé Fernet (32 octets base64) à partir du nom d'utilisateur local.
|
|
61
|
+
"""
|
|
62
|
+
username = getpass.getuser().encode("utf-8")
|
|
63
|
+
digest = hashlib.sha256(username).digest()
|
|
64
|
+
return base64.urlsafe_b64encode(digest[:32]) # 32 bytes en base64
|
|
65
|
+
|
|
66
|
+
def encrypt_secure(data: str) -> str:
|
|
67
|
+
fernet = Fernet(get_fernet_key())
|
|
68
|
+
return fernet.encrypt(data.encode("utf-8")).decode("utf-8")
|
|
69
|
+
|
|
70
|
+
def decrypt_secure(data: str) -> str:
|
|
71
|
+
fernet = Fernet(get_fernet_key())
|
|
72
|
+
return fernet.decrypt(data.encode("utf-8")).decode("utf-8")
|
|
56
73
|
|
|
57
74
|
|
|
58
75
|
# GSTION IMAP
|
|
@@ -160,6 +177,83 @@ def enregistrer_config_owa(mail,alias,server,username,password,interval):
|
|
|
160
177
|
print(f"❌ Erreur lors de l'enregistrement : {e}")
|
|
161
178
|
return 1
|
|
162
179
|
|
|
180
|
+
def enregistrer_config_owa_secure(mail, alias, server, username, password, interval):
|
|
181
|
+
try:
|
|
182
|
+
dossier = get_keys_dir("MICROSOFT_EXCHANGE_OWA_SECURE")
|
|
183
|
+
os.makedirs(dossier, exist_ok=True)
|
|
184
|
+
|
|
185
|
+
mdp_chiffre = encrypt_secure(password)
|
|
186
|
+
|
|
187
|
+
nom_fichier = os.path.join(dossier, f"{alias.replace('@', '_at_')}.json")
|
|
188
|
+
|
|
189
|
+
contenu = {
|
|
190
|
+
"mail": mail,
|
|
191
|
+
"alias": alias,
|
|
192
|
+
"server": server,
|
|
193
|
+
"username": username,
|
|
194
|
+
"password_encrypted": mdp_chiffre,
|
|
195
|
+
"interval_second": interval
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
with open(nom_fichier, "w", encoding="utf-8") as f:
|
|
199
|
+
json.dump(contenu, f, indent=4)
|
|
200
|
+
|
|
201
|
+
print(f"✅ Fichier OWA sécurisé enregistré : {nom_fichier}")
|
|
202
|
+
return 0
|
|
203
|
+
|
|
204
|
+
except Exception as e:
|
|
205
|
+
print(f"❌ Erreur d'enregistrement sécurisé OWA : {e}")
|
|
206
|
+
return 1
|
|
207
|
+
|
|
208
|
+
def enregistrer_config_cli_owa_secure():
|
|
209
|
+
print("\n🔐 Écriture fichier OWA avec chiffrement sécurisé :")
|
|
210
|
+
mail = input("📧 Mail (nom@domain.com) : ").strip()
|
|
211
|
+
alias = mail #input("📛 Alias (visible) : ").strip()
|
|
212
|
+
server = input("🌐 Server : ").strip()
|
|
213
|
+
username = input("👤 Username (domain\\user) : ").strip()
|
|
214
|
+
password = input("🔑 Password (non masqué) : ").strip()
|
|
215
|
+
interval = int(input("⏱️ Interval (secondes) : ").strip())
|
|
216
|
+
if alias == "''" or alias == "\"\"" or alias == "":
|
|
217
|
+
alias = mail
|
|
218
|
+
|
|
219
|
+
if 0 != enregistrer_config_owa_secure(mail, alias, server, username, password, interval):
|
|
220
|
+
print("❌ Erreur lors de enregistrer_config_owa_secure !")
|
|
221
|
+
|
|
222
|
+
def lecture_config_cli_owa_secure():
|
|
223
|
+
fichier = input("📄 Nom du fichier JSON (sans chemin) : ").strip()
|
|
224
|
+
cfg = lire_config_owa_secure(fichier)
|
|
225
|
+
|
|
226
|
+
if cfg is None:
|
|
227
|
+
print("❌ Erreur")
|
|
228
|
+
return
|
|
229
|
+
|
|
230
|
+
print(f"\n📧 mail : {cfg[0]}")
|
|
231
|
+
print(f"📛 alias : {cfg[1]}")
|
|
232
|
+
print(f"🌐 server : {cfg[2]}")
|
|
233
|
+
print(f"👤 username : {cfg[3]}")
|
|
234
|
+
print(f"🔑 password : {cfg[4]}")
|
|
235
|
+
print(f"⏱️ intervalle : {cfg[5]}s")
|
|
236
|
+
|
|
237
|
+
def lire_config_owa_secure(chemin_fichier):
|
|
238
|
+
try:
|
|
239
|
+
chemin_fichier = os.path.join(get_keys_dir("MICROSOFT_EXCHANGE_OWA_SECURE"), chemin_fichier)
|
|
240
|
+
with open(chemin_fichier, "r", encoding="utf-8") as f:
|
|
241
|
+
contenu = json.load(f)
|
|
242
|
+
|
|
243
|
+
mdp_dechiffre = decrypt_secure(contenu["password_encrypted"])
|
|
244
|
+
|
|
245
|
+
return [
|
|
246
|
+
contenu["mail"],
|
|
247
|
+
contenu["alias"],
|
|
248
|
+
contenu["server"],
|
|
249
|
+
contenu["username"],
|
|
250
|
+
mdp_dechiffre,
|
|
251
|
+
int(contenu["interval_second"])
|
|
252
|
+
]
|
|
253
|
+
|
|
254
|
+
except Exception as e:
|
|
255
|
+
print(f"❌ Erreur de lecture sécurisée OWA : {e}")
|
|
256
|
+
return None
|
|
163
257
|
|
|
164
258
|
|
|
165
259
|
# Fonction pour lire le fichier de configuration et déchiffrer le mot de passe
|
|
@@ -357,21 +451,28 @@ def lire_config_api(service_name):
|
|
|
357
451
|
def enregistrer_config_cli_api():
|
|
358
452
|
print("\n📝 Écriture d’une clé API :")
|
|
359
453
|
service = input("🔖 Nom du service : ").strip()
|
|
360
|
-
api_key =
|
|
454
|
+
api_key = input("🔑 Clé API : ").strip()
|
|
361
455
|
desc = input("✏️ Description : ").strip()
|
|
362
456
|
if 0 != enregistrer_config_api(service, api_key, desc):
|
|
363
457
|
print("erreur!")
|
|
364
458
|
|
|
365
|
-
def lire_config_cli_api():
|
|
366
|
-
service
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
print(f"
|
|
459
|
+
def lire_config_cli_api(service=""):
|
|
460
|
+
if service == "":
|
|
461
|
+
service = input("🔖 Nom du service : ").strip()
|
|
462
|
+
try:
|
|
463
|
+
cfg = lire_config_api(service)
|
|
464
|
+
if cfg is None:
|
|
465
|
+
print("erreur")
|
|
466
|
+
return
|
|
467
|
+
print(f"\n📄 service : {cfg['service']}")
|
|
468
|
+
print(f"🔑 clé API : {cfg['api_key']}")
|
|
469
|
+
if cfg['description']:
|
|
470
|
+
print(f"📝 description : {cfg['description']}")
|
|
471
|
+
return cfg['api_key']
|
|
472
|
+
except Exception as e:
|
|
473
|
+
print(f"❌ Erreur lors de la lecture : {e}")
|
|
474
|
+
return None
|
|
475
|
+
|
|
375
476
|
|
|
376
477
|
|
|
377
478
|
# Gestion d’éléments de nxp (DOSSIER_NODE_ID, SERVEUR, USERNAME, PASSWORD) (HARD dossier aait_store/keys)
|
|
@@ -458,8 +559,6 @@ def lire_config_cli_nxp():
|
|
|
458
559
|
print(f"📝 description : {cfg['description']}")
|
|
459
560
|
|
|
460
561
|
|
|
461
|
-
|
|
462
|
-
|
|
463
562
|
if __name__ == "__main__":
|
|
464
563
|
print("1) ecrire fichier IMAP4_SSL")
|
|
465
564
|
print("2) dechiffer fichier IMAP4_SSL")
|
|
@@ -471,7 +570,10 @@ if __name__ == "__main__":
|
|
|
471
570
|
print("8) Déchiffrer fichier Microsoft Exchange (OWA)")
|
|
472
571
|
print("9) Écrire fichier Microsoft Exchange (OAuth2)")
|
|
473
572
|
print("10) Déchiffrer fichier Microsoft Exchange (OAuth2)")
|
|
474
|
-
|
|
573
|
+
print("11) Écrire fichier Microsoft Exchange (OWA) [SECURE]")
|
|
574
|
+
print("12) Déchiffrer fichier Microsoft Exchange (OWA) [SECURE]")
|
|
575
|
+
|
|
576
|
+
choix = input("👉 Que faire ? [1-12] : ").strip()
|
|
475
577
|
|
|
476
578
|
if choix == "1":
|
|
477
579
|
enregistrer_config_cli_imap4_ssl()
|
|
@@ -492,7 +594,12 @@ if __name__ == "__main__":
|
|
|
492
594
|
elif choix=="9":
|
|
493
595
|
enregistrer_config_cli_oauth2()
|
|
494
596
|
elif choix=="10":
|
|
495
|
-
|
|
597
|
+
lire_config_cli_oauth2()
|
|
598
|
+
elif choix == "11":
|
|
599
|
+
enregistrer_config_cli_owa_secure()
|
|
600
|
+
elif choix == "12":
|
|
601
|
+
lecture_config_cli_owa_secure()
|
|
602
|
+
|
|
496
603
|
|
|
497
604
|
else:
|
|
498
605
|
print("❌ Choix invalide. Réessayez.\n")
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# pool_exec_utils.py
|
|
2
|
+
import os
|
|
3
|
+
from concurrent.futures import ProcessPoolExecutor
|
|
4
|
+
from typing import Optional, Tuple
|
|
5
|
+
|
|
6
|
+
def available_cpus(default: int = 4) -> int:
|
|
7
|
+
c = os.cpu_count()
|
|
8
|
+
return c if (c and c > 0) else default
|
|
9
|
+
|
|
10
|
+
def cpu_label_text() -> str:
|
|
11
|
+
return f"🖥️ CPU dispo : {available_cpus()}"
|
|
12
|
+
|
|
13
|
+
def shutdown_executor(executor: Optional[ProcessPoolExecutor]) -> None:
|
|
14
|
+
if executor is not None:
|
|
15
|
+
try:
|
|
16
|
+
executor.shutdown(wait=False, cancel_futures=True)
|
|
17
|
+
except TypeError:
|
|
18
|
+
executor.shutdown(wait=False)
|
|
19
|
+
except Exception:
|
|
20
|
+
pass
|
|
21
|
+
|
|
22
|
+
def create_or_update_executor(
|
|
23
|
+
executor: Optional[ProcessPoolExecutor],
|
|
24
|
+
current_workers: Optional[int],
|
|
25
|
+
new_workers: int,
|
|
26
|
+
) -> Tuple[ProcessPoolExecutor, int, str, bool]:
|
|
27
|
+
changed = (executor is None) or (current_workers != int(new_workers))
|
|
28
|
+
if changed:
|
|
29
|
+
shutdown_executor(executor)
|
|
30
|
+
executor = ProcessPoolExecutor(max_workers=int(new_workers))
|
|
31
|
+
return executor, int(new_workers), f"Executor prêt ({int(new_workers)} workers).", True
|
|
32
|
+
else:
|
|
33
|
+
return executor, int(current_workers), f"Executor inchangé ({int(new_workers)} workers).", False
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
# utils_md.py
|
|
2
|
+
import os, re, hashlib, secrets, urllib.parse, logging, time, datetime, numpy as np
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
import fitz # PyMuPDF
|
|
5
|
+
|
|
6
|
+
try:
|
|
7
|
+
import easyocr
|
|
8
|
+
except Exception:
|
|
9
|
+
easyocr = None
|
|
10
|
+
|
|
11
|
+
MAX_STEM_LEN = 40
|
|
12
|
+
IMAGE_RESOLUTION_SCALE = 2.0
|
|
13
|
+
|
|
14
|
+
_log = logging.getLogger(__name__)
|
|
15
|
+
logging.getLogger("fitz").setLevel(logging.ERROR)
|
|
16
|
+
logging.getLogger("PIL").setLevel(logging.WARNING)
|
|
17
|
+
|
|
18
|
+
def slugify(text: str) -> str:
|
|
19
|
+
text = text.strip().lower()
|
|
20
|
+
text = re.sub(r"[^\w\-]+", "-", text)
|
|
21
|
+
text = re.sub(r"-{2,}", "-", text)
|
|
22
|
+
return text.strip("-") or "file"
|
|
23
|
+
|
|
24
|
+
def short_stem(original_stem: str) -> str:
|
|
25
|
+
base = slugify(original_stem)[:MAX_STEM_LEN]
|
|
26
|
+
h = secrets.token_hex(6)
|
|
27
|
+
return f"{base}-{h}"
|
|
28
|
+
|
|
29
|
+
def truncate_path(path, max_length=240):
|
|
30
|
+
p = Path(path)
|
|
31
|
+
s = str(p)
|
|
32
|
+
if len(s) <= max_length:
|
|
33
|
+
return p
|
|
34
|
+
h = hashlib.md5(s.encode()).hexdigest()
|
|
35
|
+
new_name = (p.stem[:50] + "_" + h + p.suffix)
|
|
36
|
+
return p.parent / new_name
|
|
37
|
+
|
|
38
|
+
def try_read_text(path: Path) -> str:
|
|
39
|
+
for enc in ("utf-8", "utf-8-sig", "latin-1"):
|
|
40
|
+
try:
|
|
41
|
+
return urllib.parse.unquote(path.read_text(encoding=enc, errors="ignore"))
|
|
42
|
+
except Exception:
|
|
43
|
+
continue
|
|
44
|
+
return ""
|
|
45
|
+
|
|
46
|
+
def is_pdf_text_based(fpath: Path) -> bool:
|
|
47
|
+
try:
|
|
48
|
+
with fitz.open(fpath) as doc:
|
|
49
|
+
for page in doc:
|
|
50
|
+
if page.get_text().strip():
|
|
51
|
+
return True
|
|
52
|
+
return False
|
|
53
|
+
except Exception:
|
|
54
|
+
return False
|
|
55
|
+
|
|
56
|
+
def ocr_fallback(pdf_path: Path, langs=('fr','en')) -> str:
|
|
57
|
+
if easyocr is None:
|
|
58
|
+
raise RuntimeError("easyocr introuvable. Installez easyocr pour l'OCR.")
|
|
59
|
+
reader = easyocr.Reader(list(langs))
|
|
60
|
+
_ = reader.readtext(np.zeros((10, 10, 3), dtype=np.uint8), detail=0) # warm-up
|
|
61
|
+
text = []
|
|
62
|
+
with fitz.open(pdf_path) as doc:
|
|
63
|
+
for page in doc:
|
|
64
|
+
pix = page.get_pixmap(dpi=200)
|
|
65
|
+
img = np.frombuffer(pix.samples, dtype=np.uint8).reshape((pix.height, pix.width, pix.n))
|
|
66
|
+
result = reader.readtext(img)
|
|
67
|
+
text.extend([r[1] for r in result])
|
|
68
|
+
return "\n".join(text)
|
|
69
|
+
|
|
70
|
+
def strip_image_markdown(md_text: str) -> str:
|
|
71
|
+
md_text = re.sub(r'!\[[^\]]*\]\([^\)]+\)', '', md_text)
|
|
72
|
+
md_text = re.sub(r'<img\b[^>]*>', '', md_text, flags=re.IGNORECASE)
|
|
73
|
+
md_text = re.sub(r'\n{3,}', '\n\n', md_text)
|
|
74
|
+
return md_text.strip()
|
|
75
|
+
|
|
76
|
+
# --- COM helpers (Windows + Office)
|
|
77
|
+
def _make_word_invisible(word):
|
|
78
|
+
try:
|
|
79
|
+
word.Visible = False
|
|
80
|
+
word.DisplayAlerts = 0
|
|
81
|
+
word.ScreenUpdating = False
|
|
82
|
+
except Exception:
|
|
83
|
+
pass
|
|
84
|
+
|
|
85
|
+
def _make_powerpoint_invisible(ppt):
|
|
86
|
+
try:
|
|
87
|
+
ppt.Visible = 0 # 0=Hidden
|
|
88
|
+
except Exception:
|
|
89
|
+
pass
|
|
90
|
+
|
|
91
|
+
def convert_doc_to_docx(src: Path, out_dir: Path) -> Path:
|
|
92
|
+
import win32com.client
|
|
93
|
+
out_dir.mkdir(parents=True, exist_ok=True)
|
|
94
|
+
if src.suffix.lower() != ".doc":
|
|
95
|
+
return src if src.suffix.lower() == ".docx" else src
|
|
96
|
+
dst = out_dir / (src.stem + ".docx")
|
|
97
|
+
if dst.exists() and dst.stat().st_size > 0:
|
|
98
|
+
return dst
|
|
99
|
+
word = win32com.client.Dispatch("Word.Application")
|
|
100
|
+
_make_word_invisible(word)
|
|
101
|
+
try:
|
|
102
|
+
doc = word.Documents.Open(str(src), ReadOnly=True)
|
|
103
|
+
doc.SaveAs(str(dst), FileFormat=16) # DOCX
|
|
104
|
+
doc.Close(False)
|
|
105
|
+
if not dst.exists() or dst.stat().st_size == 0:
|
|
106
|
+
raise RuntimeError("DOCX non généré")
|
|
107
|
+
return dst
|
|
108
|
+
finally:
|
|
109
|
+
word.Quit()
|
|
110
|
+
|
|
111
|
+
def convert_ppt_to_pptx(src: Path, out_dir: Path) -> Path:
|
|
112
|
+
import win32com.client
|
|
113
|
+
out_dir.mkdir(parents=True, exist_ok=True)
|
|
114
|
+
if src.suffix.lower() != ".ppt":
|
|
115
|
+
return src if src.suffix.lower() == ".pptx" else src
|
|
116
|
+
dst = out_dir / (src.stem + ".pptx")
|
|
117
|
+
if dst.exists() and dst.stat().st_size > 0:
|
|
118
|
+
return dst
|
|
119
|
+
ppt = win32com.client.Dispatch("PowerPoint.Application")
|
|
120
|
+
_make_powerpoint_invisible(ppt)
|
|
121
|
+
try:
|
|
122
|
+
pres = ppt.Presentations.Open(str(src), WithWindow=False)
|
|
123
|
+
pres.SaveAs(str(dst), 24) # ppSaveAsOpenXMLPresentation
|
|
124
|
+
pres.Close()
|
|
125
|
+
if not dst.exists() or dst.stat().st_size == 0:
|
|
126
|
+
raise RuntimeError("PPTX non généré")
|
|
127
|
+
return dst
|
|
128
|
+
finally:
|
|
129
|
+
ppt.Quit()
|
|
130
|
+
|
|
131
|
+
def docx_to_pdf(src_docx: Path, out_dir: Path) -> Path:
|
|
132
|
+
import win32com.client
|
|
133
|
+
out_dir.mkdir(parents=True, exist_ok=True)
|
|
134
|
+
pdf_path = out_dir / (src_docx.stem + ".pdf")
|
|
135
|
+
if pdf_path.exists() and pdf_path.stat().st_size > 0:
|
|
136
|
+
return pdf_path
|
|
137
|
+
word = win32com.client.Dispatch("Word.Application")
|
|
138
|
+
_make_word_invisible(word)
|
|
139
|
+
try:
|
|
140
|
+
doc = word.Documents.Open(str(src_docx), ReadOnly=True)
|
|
141
|
+
try:
|
|
142
|
+
doc.ExportAsFixedFormat(OutputFileName=str(pdf_path), ExportFormat=17)
|
|
143
|
+
except Exception:
|
|
144
|
+
doc.SaveAs(str(pdf_path), FileFormat=17)
|
|
145
|
+
doc.Close(False)
|
|
146
|
+
if not pdf_path.exists() or pdf_path.stat().st_size == 0:
|
|
147
|
+
raise RuntimeError("PDF non généré depuis DOCX")
|
|
148
|
+
return pdf_path
|
|
149
|
+
finally:
|
|
150
|
+
word.Quit()
|
|
151
|
+
|
|
152
|
+
def pptx_to_pdf(src_pptx: Path, out_dir: Path) -> Path:
|
|
153
|
+
import win32com.client
|
|
154
|
+
out_dir.mkdir(parents=True, exist_ok=True)
|
|
155
|
+
pdf_path = out_dir / (src_pptx.stem + ".pdf")
|
|
156
|
+
if pdf_path.exists() and pdf_path.stat().st_size > 0:
|
|
157
|
+
return pdf_path
|
|
158
|
+
ppt = win32com.client.Dispatch("PowerPoint.Application")
|
|
159
|
+
_make_powerpoint_invisible(ppt)
|
|
160
|
+
try:
|
|
161
|
+
pres = ppt.Presentations.Open(str(src_pptx), WithWindow=False)
|
|
162
|
+
pres.SaveAs(str(pdf_path), 32) # PDF
|
|
163
|
+
pres.Close()
|
|
164
|
+
if not pdf_path.exists() or pdf_path.stat().st_size == 0:
|
|
165
|
+
raise RuntimeError("PDF non généré depuis PPTX")
|
|
166
|
+
return pdf_path
|
|
167
|
+
finally:
|
|
168
|
+
ppt.Quit()
|
|
@@ -165,7 +165,7 @@ class OWExportMarkdown(widget.OWWidget):
|
|
|
165
165
|
|
|
166
166
|
try:
|
|
167
167
|
# DOCX
|
|
168
|
-
pypandoc.convert_file(tmp_md, to="docx", outputfile=docx_out)
|
|
168
|
+
pypandoc.convert_file(tmp_md, to="docx", format="gfm-yaml_metadata_block", outputfile=docx_out)
|
|
169
169
|
self.ajouter_en_tete_pied_docx(
|
|
170
170
|
docx_out,
|
|
171
171
|
"Rapport - Orange AI",
|
|
@@ -173,7 +173,7 @@ class OWExportMarkdown(widget.OWWidget):
|
|
|
173
173
|
)
|
|
174
174
|
|
|
175
175
|
# PPTX
|
|
176
|
-
pypandoc.convert_file(tmp_md, to="pptx", outputfile=pptx_out)
|
|
176
|
+
pypandoc.convert_file(tmp_md, to="pptx", format="gfm-yaml_metadata_block", outputfile=pptx_out)
|
|
177
177
|
self.ajouter_entete_pied_pptx(
|
|
178
178
|
pptx_out,
|
|
179
179
|
"Orange AI – Présentation",
|