io4it 1.0.1__tar.gz → 1.0.2__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-1.0.1 → io4it-1.0.2}/PKG-INFO +3 -1
- {io4it-1.0.1 → io4it-1.0.2}/io4it.egg-info/PKG-INFO +3 -1
- {io4it-1.0.1 → io4it-1.0.2}/io4it.egg-info/SOURCES.txt +13 -0
- {io4it-1.0.1 → io4it-1.0.2}/io4it.egg-info/requires.txt +2 -0
- io4it-1.0.2/orangecontrib/IO4IT/utils/mail.py +241 -0
- io4it-1.0.2/orangecontrib/IO4IT/utils/offuscation_basique.py +126 -0
- io4it-1.0.2/orangecontrib/IO4IT/widgets/OWDeep_Search.py +208 -0
- io4it-1.0.2/orangecontrib/IO4IT/widgets/OWExportMarkdown.py +150 -0
- {io4it-1.0.1 → io4it-1.0.2}/orangecontrib/IO4IT/widgets/OWS3Uploader.py +8 -1
- {io4it-1.0.1 → io4it-1.0.2}/orangecontrib/IO4IT/widgets/OWS3downloader.py +1 -1
- {io4it-1.0.1 → io4it-1.0.2}/orangecontrib/IO4IT/widgets/OWS3list.py +1 -1
- {io4it-1.0.1 → io4it-1.0.2}/orangecontrib/IO4IT/widgets/OWSpeechToText.py +3 -1
- io4it-1.0.2/orangecontrib/IO4IT/widgets/OWmailLoader.py +207 -0
- io4it-1.0.2/orangecontrib/IO4IT/widgets/OWmailSender.py +150 -0
- io4it-1.0.2/orangecontrib/IO4IT/widgets/designer/owexportmarkdown.ui +59 -0
- {io4it-1.0.1 → io4it-1.0.2}/orangecontrib/IO4IT/widgets/designer/owmailloader.ui +1 -1
- io4it-1.0.2/orangecontrib/IO4IT/widgets/designer/owmailsender.ui +32 -0
- io4it-1.0.2/orangecontrib/IO4IT/widgets/icons/deepsearch.svg +10 -0
- io4it-1.0.2/orangecontrib/IO4IT/widgets/icons/export_md.png +0 -0
- io4it-1.0.2/orangecontrib/IO4IT/widgets/icons/mail_loader.png +0 -0
- io4it-1.0.2/orangecontrib/IO4IT/widgets/icons/mail_writer.png +0 -0
- io4it-1.0.2/orangecontrib/IO4IT/widgets/icons_dev/__init__.py +0 -0
- {io4it-1.0.1 → io4it-1.0.2}/setup.py +3 -1
- {io4it-1.0.1 → io4it-1.0.2}/io4it.egg-info/dependency_links.txt +0 -0
- {io4it-1.0.1 → io4it-1.0.2}/io4it.egg-info/entry_points.txt +0 -0
- {io4it-1.0.1 → io4it-1.0.2}/io4it.egg-info/namespace_packages.txt +0 -0
- {io4it-1.0.1 → io4it-1.0.2}/io4it.egg-info/top_level.txt +0 -0
- {io4it-1.0.1 → io4it-1.0.2}/orangecontrib/IO4IT/__init__.py +0 -0
- {io4it-1.0.1 → io4it-1.0.2}/orangecontrib/IO4IT/ocr_function/__init__.py +0 -0
- {io4it-1.0.1 → io4it-1.0.2}/orangecontrib/IO4IT/ocr_function/word_converter.py +0 -0
- {io4it-1.0.1/orangecontrib/IO4IT/widgets/designer → io4it-1.0.2/orangecontrib/IO4IT/utils}/__init__.py +0 -0
- {io4it-1.0.1 → io4it-1.0.2}/orangecontrib/IO4IT/widgets/OWMarkdownizer.py +0 -0
- {io4it-1.0.1 → io4it-1.0.2}/orangecontrib/IO4IT/widgets/OWwordpdf2docx.py +0 -0
- {io4it-1.0.1 → io4it-1.0.2}/orangecontrib/IO4IT/widgets/__init__.py +0 -0
- {io4it-1.0.1/orangecontrib/IO4IT/widgets/icons → io4it-1.0.2/orangecontrib/IO4IT/widgets/designer}/__init__.py +0 -0
- {io4it-1.0.1 → io4it-1.0.2}/orangecontrib/IO4IT/widgets/designer/chart.html +0 -0
- {io4it-1.0.1 → io4it-1.0.2}/orangecontrib/IO4IT/widgets/designer/nogui.ui +0 -0
- {io4it-1.0.1 → io4it-1.0.2}/orangecontrib/IO4IT/widgets/designer/ow_file_ext_selector.ui +0 -0
- {io4it-1.0.1 → io4it-1.0.2}/orangecontrib/IO4IT/widgets/designer/owspeechtotext.ui +0 -0
- {io4it-1.0.1 → io4it-1.0.2}/orangecontrib/IO4IT/widgets/designer/owvisualizationer.ui +0 -0
- {io4it-1.0.1 → io4it-1.0.2}/orangecontrib/IO4IT/widgets/designer/wordpdf2docx.ui +0 -0
- {io4it-1.0.1/orangecontrib/IO4IT/widgets/icons_dev → io4it-1.0.2/orangecontrib/IO4IT/widgets/icons}/__init__.py +0 -0
- {io4it-1.0.1 → io4it-1.0.2}/orangecontrib/IO4IT/widgets/icons/download.png +0 -0
- {io4it-1.0.1 → io4it-1.0.2}/orangecontrib/IO4IT/widgets/icons/file_extensor.png +0 -0
- {io4it-1.0.1 → io4it-1.0.2}/orangecontrib/IO4IT/widgets/icons/list_aws.png +0 -0
- {io4it-1.0.1 → io4it-1.0.2}/orangecontrib/IO4IT/widgets/icons/md.png +0 -0
- {io4it-1.0.1 → io4it-1.0.2}/orangecontrib/IO4IT/widgets/icons/speech_to_text.png +0 -0
- {io4it-1.0.1 → io4it-1.0.2}/orangecontrib/IO4IT/widgets/icons/upload.png +0 -0
- {io4it-1.0.1 → io4it-1.0.2}/orangecontrib/IO4IT/widgets/icons/visualizationer.png +0 -0
- {io4it-1.0.1 → io4it-1.0.2}/orangecontrib/IO4IT/widgets/icons/wordpdf2docx.png +0 -0
- {io4it-1.0.1 → io4it-1.0.2}/orangecontrib/__init__.py +0 -0
- {io4it-1.0.1 → io4it-1.0.2}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: io4it
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.2
|
|
4
4
|
Home-page:
|
|
5
5
|
Author:
|
|
6
6
|
Author-email:
|
|
@@ -15,5 +15,7 @@ Requires-Dist: pyannote.audio
|
|
|
15
15
|
Requires-Dist: pyannote-core
|
|
16
16
|
Requires-Dist: wave
|
|
17
17
|
Requires-Dist: scikit-learn
|
|
18
|
+
Requires-Dist: pylatexenc
|
|
19
|
+
Requires-Dist: docopt
|
|
18
20
|
Dynamic: keywords
|
|
19
21
|
Dynamic: requires-dist
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: io4it
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.2
|
|
4
4
|
Home-page:
|
|
5
5
|
Author:
|
|
6
6
|
Author-email:
|
|
@@ -15,5 +15,7 @@ Requires-Dist: pyannote.audio
|
|
|
15
15
|
Requires-Dist: pyannote-core
|
|
16
16
|
Requires-Dist: wave
|
|
17
17
|
Requires-Dist: scikit-learn
|
|
18
|
+
Requires-Dist: pylatexenc
|
|
19
|
+
Requires-Dist: docopt
|
|
18
20
|
Dynamic: keywords
|
|
19
21
|
Dynamic: requires-dist
|
|
@@ -11,25 +11,38 @@ orangecontrib/__init__.py
|
|
|
11
11
|
orangecontrib/IO4IT/__init__.py
|
|
12
12
|
orangecontrib/IO4IT/ocr_function/__init__.py
|
|
13
13
|
orangecontrib/IO4IT/ocr_function/word_converter.py
|
|
14
|
+
orangecontrib/IO4IT/utils/__init__.py
|
|
15
|
+
orangecontrib/IO4IT/utils/mail.py
|
|
16
|
+
orangecontrib/IO4IT/utils/offuscation_basique.py
|
|
17
|
+
orangecontrib/IO4IT/widgets/OWDeep_Search.py
|
|
18
|
+
orangecontrib/IO4IT/widgets/OWExportMarkdown.py
|
|
14
19
|
orangecontrib/IO4IT/widgets/OWMarkdownizer.py
|
|
15
20
|
orangecontrib/IO4IT/widgets/OWS3Uploader.py
|
|
16
21
|
orangecontrib/IO4IT/widgets/OWS3downloader.py
|
|
17
22
|
orangecontrib/IO4IT/widgets/OWS3list.py
|
|
18
23
|
orangecontrib/IO4IT/widgets/OWSpeechToText.py
|
|
24
|
+
orangecontrib/IO4IT/widgets/OWmailLoader.py
|
|
25
|
+
orangecontrib/IO4IT/widgets/OWmailSender.py
|
|
19
26
|
orangecontrib/IO4IT/widgets/OWwordpdf2docx.py
|
|
20
27
|
orangecontrib/IO4IT/widgets/__init__.py
|
|
21
28
|
orangecontrib/IO4IT/widgets/designer/__init__.py
|
|
22
29
|
orangecontrib/IO4IT/widgets/designer/chart.html
|
|
23
30
|
orangecontrib/IO4IT/widgets/designer/nogui.ui
|
|
24
31
|
orangecontrib/IO4IT/widgets/designer/ow_file_ext_selector.ui
|
|
32
|
+
orangecontrib/IO4IT/widgets/designer/owexportmarkdown.ui
|
|
25
33
|
orangecontrib/IO4IT/widgets/designer/owmailloader.ui
|
|
34
|
+
orangecontrib/IO4IT/widgets/designer/owmailsender.ui
|
|
26
35
|
orangecontrib/IO4IT/widgets/designer/owspeechtotext.ui
|
|
27
36
|
orangecontrib/IO4IT/widgets/designer/owvisualizationer.ui
|
|
28
37
|
orangecontrib/IO4IT/widgets/designer/wordpdf2docx.ui
|
|
29
38
|
orangecontrib/IO4IT/widgets/icons/__init__.py
|
|
39
|
+
orangecontrib/IO4IT/widgets/icons/deepsearch.svg
|
|
30
40
|
orangecontrib/IO4IT/widgets/icons/download.png
|
|
41
|
+
orangecontrib/IO4IT/widgets/icons/export_md.png
|
|
31
42
|
orangecontrib/IO4IT/widgets/icons/file_extensor.png
|
|
32
43
|
orangecontrib/IO4IT/widgets/icons/list_aws.png
|
|
44
|
+
orangecontrib/IO4IT/widgets/icons/mail_loader.png
|
|
45
|
+
orangecontrib/IO4IT/widgets/icons/mail_writer.png
|
|
33
46
|
orangecontrib/IO4IT/widgets/icons/md.png
|
|
34
47
|
orangecontrib/IO4IT/widgets/icons/speech_to_text.png
|
|
35
48
|
orangecontrib/IO4IT/widgets/icons/upload.png
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
import imaplib
|
|
2
|
+
import smtplib
|
|
3
|
+
import email
|
|
4
|
+
from email.header import decode_header
|
|
5
|
+
import time
|
|
6
|
+
import os
|
|
7
|
+
from email import policy
|
|
8
|
+
from email.message import EmailMessage
|
|
9
|
+
if "site-packages/Orange/widgets" in os.path.dirname(os.path.abspath(__file__)).replace("\\", "/"):
|
|
10
|
+
from Orange.widgets.orangecontrib.AAIT.utils import MetManagement
|
|
11
|
+
from Orange.widgets.orangecontrib.IO4IT.utils import offuscation_basique
|
|
12
|
+
else:
|
|
13
|
+
from orangecontrib.AAIT.utils import MetManagement
|
|
14
|
+
from orangecontrib.IO4IT.utils import offuscation_basique
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def clean_addresses(field,myemail):
|
|
18
|
+
"""Retourne une liste d'adresses nettoyées sans ton adresse"""
|
|
19
|
+
if not field:
|
|
20
|
+
return []
|
|
21
|
+
addresses = email.utils.getaddresses([field])
|
|
22
|
+
return [addr for name, addr in addresses if addr.lower() != myemail.lower()]
|
|
23
|
+
|
|
24
|
+
def mail_in_folder(agent_name, type="in"):
|
|
25
|
+
if agent_name is None or agent_name == "":
|
|
26
|
+
print("agent_name doit etre renseigné")
|
|
27
|
+
return None
|
|
28
|
+
chemin_dossier= MetManagement.get_path_mailFolder()
|
|
29
|
+
if type == "in":
|
|
30
|
+
if not os.path.exists(chemin_dossier):
|
|
31
|
+
os.makedirs(chemin_dossier)
|
|
32
|
+
real_time = MetManagement.get_second_from_1970()
|
|
33
|
+
folder_in = chemin_dossier + "/" + str(agent_name) + "/in/" + str(real_time)
|
|
34
|
+
folder_out = chemin_dossier + "/" + str(agent_name) + "/out/" + str(real_time)
|
|
35
|
+
if not os.path.exists(folder_in) and not os.path.exists(folder_out):
|
|
36
|
+
os.makedirs(folder_in)
|
|
37
|
+
os.makedirs(folder_out)
|
|
38
|
+
else:
|
|
39
|
+
time.sleep(1.5)
|
|
40
|
+
mail_in_folder(agent_name, "in")
|
|
41
|
+
return folder_in
|
|
42
|
+
if type == "out":
|
|
43
|
+
return chemin_dossier + str(agent_name) + "/in/", chemin_dossier + "/" + str(agent_name) + "/out/"
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def check_new_emails(offusc_conf_agent):
|
|
47
|
+
try:
|
|
48
|
+
agent,my_domain,password,interl_seconds=offuscation_basique.lire_config(offusc_conf_agent)
|
|
49
|
+
myemail=agent + my_domain
|
|
50
|
+
imap = imaplib.IMAP4_SSL("imap.gmail.com")
|
|
51
|
+
imap.login(myemail, password)
|
|
52
|
+
imap.select("inbox")
|
|
53
|
+
|
|
54
|
+
status, messages = imap.search(None, 'UNSEEN')
|
|
55
|
+
mail_ids = messages[0].split()
|
|
56
|
+
|
|
57
|
+
if not mail_ids:
|
|
58
|
+
print("Aucun nouveau mail.")
|
|
59
|
+
else:
|
|
60
|
+
for mail_id in mail_ids:
|
|
61
|
+
time.sleep(1.5)
|
|
62
|
+
output_lines = []
|
|
63
|
+
_, msg_data = imap.fetch(mail_id, "(RFC822)")
|
|
64
|
+
for response_part in msg_data:
|
|
65
|
+
if isinstance(response_part, tuple):
|
|
66
|
+
from email.parser import BytesParser
|
|
67
|
+
|
|
68
|
+
# Utilise :
|
|
69
|
+
msg = BytesParser(policy=policy.default).parsebytes(response_part[1])
|
|
70
|
+
|
|
71
|
+
# Sujet
|
|
72
|
+
subject, encoding = decode_header(msg["Subject"])[0]
|
|
73
|
+
if isinstance(subject, bytes):
|
|
74
|
+
subject = subject.decode(encoding if encoding else "utf-8")
|
|
75
|
+
|
|
76
|
+
# Expéditeur
|
|
77
|
+
from_ = msg.get("From")
|
|
78
|
+
|
|
79
|
+
# Destinataires
|
|
80
|
+
to_emails = clean_addresses(msg.get("To", ""),myemail)
|
|
81
|
+
cc_emails = clean_addresses(msg.get("Cc", ""),myemail)
|
|
82
|
+
|
|
83
|
+
# Corps
|
|
84
|
+
body = ""
|
|
85
|
+
if msg.is_multipart():
|
|
86
|
+
for part in msg.walk():
|
|
87
|
+
content_type = part.get_content_type()
|
|
88
|
+
content_disposition = str(part.get("Content-Disposition"))
|
|
89
|
+
|
|
90
|
+
if content_type == "text/plain" and "attachment" not in content_disposition:
|
|
91
|
+
payload = part.get_payload(decode=True)
|
|
92
|
+
charset = part.get_content_charset()
|
|
93
|
+
body = payload.decode(charset if charset else "utf-8", errors="replace")
|
|
94
|
+
break
|
|
95
|
+
else:
|
|
96
|
+
body = msg.get_payload(decode=True).decode(errors="replace")
|
|
97
|
+
|
|
98
|
+
# Format de sortie
|
|
99
|
+
output_lines.append(f"#$who : {myemail}")
|
|
100
|
+
output_lines.append(f"#$eme : {from_}")
|
|
101
|
+
output_lines.append(f"#$des : {', '.join(to_emails)}")
|
|
102
|
+
output_lines.append(f"#$cop : {', '.join(cc_emails)}")
|
|
103
|
+
output_lines.append("#$pri : Normale")
|
|
104
|
+
output_lines.append(f"#$tit : {subject}")
|
|
105
|
+
output_lines.append(f"#$txt : {body.strip()}")
|
|
106
|
+
output_lines.append("")
|
|
107
|
+
print("----------------------------------------")
|
|
108
|
+
print(f"mail recu de {from_}")
|
|
109
|
+
print("----------------------------------------")
|
|
110
|
+
|
|
111
|
+
if output_lines != []:
|
|
112
|
+
folder = mail_in_folder(agent, "in")
|
|
113
|
+
if folder is None:
|
|
114
|
+
print("erreur dans le folder de mail")
|
|
115
|
+
return
|
|
116
|
+
|
|
117
|
+
with open(folder + "/" + "mail.txt", "w", encoding="utf-8") as f:
|
|
118
|
+
f.write("\n".join(output_lines))
|
|
119
|
+
f.close()
|
|
120
|
+
|
|
121
|
+
with open(folder + "/" + "mail.ok", "w") as f:
|
|
122
|
+
f.close()
|
|
123
|
+
|
|
124
|
+
for part in msg.iter_attachments():
|
|
125
|
+
filename = part.get_filename()
|
|
126
|
+
if filename:
|
|
127
|
+
folder_pj = folder + "/" + "pj"
|
|
128
|
+
if not os.path.exists(folder_pj):
|
|
129
|
+
os.makedirs(folder_pj)
|
|
130
|
+
filepath = os.path.join(folder_pj, filename)
|
|
131
|
+
with open(filepath, "wb") as f:
|
|
132
|
+
f.write(part.get_payload(decode=True))
|
|
133
|
+
f.close()
|
|
134
|
+
|
|
135
|
+
imap.logout()
|
|
136
|
+
except Exception as e:
|
|
137
|
+
print(f"Erreur lors de la vérification des mails : {e}")
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def lire_message(chemin_fichier):
|
|
141
|
+
donnees = {}
|
|
142
|
+
cle_courante = None
|
|
143
|
+
texte_multi_ligne = []
|
|
144
|
+
|
|
145
|
+
with open(chemin_fichier, 'r', encoding='utf-8') as fichier:
|
|
146
|
+
for ligne in fichier:
|
|
147
|
+
if ligne.startswith('#$'):
|
|
148
|
+
if cle_courante == 'txt' and texte_multi_ligne:
|
|
149
|
+
donnees['txt'] = '\n'.join(texte_multi_ligne).strip()
|
|
150
|
+
texte_multi_ligne = []
|
|
151
|
+
|
|
152
|
+
cle_val = ligne[2:].split(':', 1)
|
|
153
|
+
cle_courante = cle_val[0].strip()
|
|
154
|
+
|
|
155
|
+
if cle_courante == 'txt':
|
|
156
|
+
texte_multi_ligne.append(cle_val[1].strip())
|
|
157
|
+
else:
|
|
158
|
+
donnees[cle_courante] = cle_val[1].strip()
|
|
159
|
+
else:
|
|
160
|
+
if cle_courante == 'txt':
|
|
161
|
+
texte_multi_ligne.append(ligne.rstrip())
|
|
162
|
+
|
|
163
|
+
# En fin de fichier, enregistrer le texte si encore en cours
|
|
164
|
+
if cle_courante == 'txt' and texte_multi_ligne:
|
|
165
|
+
donnees['txt'] = '\n'.join(texte_multi_ligne).strip()
|
|
166
|
+
|
|
167
|
+
return donnees
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def send_mail(expediteur, offusc_conf_agent, destinataire, sujet, contenu,piece_jointe_path=None, serveur="smtp.gmail.com", port=587):
|
|
173
|
+
msg = EmailMessage()
|
|
174
|
+
msg['From'] = expediteur
|
|
175
|
+
msg['To'] = destinataire
|
|
176
|
+
msg['Subject'] = sujet
|
|
177
|
+
msg.set_content(contenu)
|
|
178
|
+
# Ajout d'une pièce jointe si fournie
|
|
179
|
+
if piece_jointe_path:
|
|
180
|
+
with open(piece_jointe_path, 'rb') as f:
|
|
181
|
+
data = f.read()
|
|
182
|
+
nom_fichier = piece_jointe_path.split('/')[-1]
|
|
183
|
+
msg.add_attachment(data, maintype='application', subtype='octet-stream', filename=nom_fichier)
|
|
184
|
+
try:
|
|
185
|
+
_, _, mot_de_passe, _ = offuscation_basique.lire_config(offusc_conf_agent)
|
|
186
|
+
with smtplib.SMTP(serveur, port) as smtp:
|
|
187
|
+
smtp.starttls()
|
|
188
|
+
smtp.login(expediteur, mot_de_passe)
|
|
189
|
+
smtp.send_message(msg)
|
|
190
|
+
return 0
|
|
191
|
+
except Exception as e:
|
|
192
|
+
print("❌ Une erreur s'est produite :", e)
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def check_send_new_emails(offusc_conf_agent):
|
|
196
|
+
agent, domain, _, _ = offuscation_basique.lire_config(offusc_conf_agent)
|
|
197
|
+
chemin_dossier_in, chemin_dossier_out = mail_in_folder(agent, "out")
|
|
198
|
+
if os.path.exists(chemin_dossier_out) and os.path.isdir(chemin_dossier_out):
|
|
199
|
+
contenus = os.listdir(chemin_dossier_out)
|
|
200
|
+
if contenus:
|
|
201
|
+
for contenu in contenus:
|
|
202
|
+
if os.path.exists(chemin_dossier_out + "/" + contenu + "/mail.ok"):
|
|
203
|
+
chemin = chemin_dossier_out + "/" + contenu + "/mail.txt"
|
|
204
|
+
infos = lire_message(chemin)
|
|
205
|
+
# Affichage des informations extraites
|
|
206
|
+
cles_requises = ["eme", "des", "cop", "pri", "tit", "txt"]
|
|
207
|
+
if all(cle in infos for cle in cles_requises):
|
|
208
|
+
send_mail(
|
|
209
|
+
agent+domain,
|
|
210
|
+
offusc_conf_agent,
|
|
211
|
+
infos["eme"],
|
|
212
|
+
infos["tit"],
|
|
213
|
+
infos["txt"],
|
|
214
|
+
piece_jointe_path=None #à rajouter quand PJ ok chemin_dossier_out + "/" + contenu + "/pj"
|
|
215
|
+
)
|
|
216
|
+
MetManagement.reset_folder(chemin_dossier_in + contenu , recreate=False)
|
|
217
|
+
MetManagement.reset_folder(chemin_dossier_out + contenu, recreate=False)
|
|
218
|
+
else:
|
|
219
|
+
print("il manque des clefs dans le contenu du mail")
|
|
220
|
+
else:
|
|
221
|
+
print("Le dossier est vide.")
|
|
222
|
+
else:
|
|
223
|
+
print("Le dossier n'existe pas ou le chemin n'est pas un dossier.")
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
if __name__ == "__main__":
|
|
227
|
+
offusc_conf_agent="agent.ia_at_institut-ia.com.json"
|
|
228
|
+
while True:
|
|
229
|
+
check_new_emails(offusc_conf_agent)
|
|
230
|
+
time.sleep(1)
|
|
231
|
+
check_send_new_emails(offusc_conf_agent)
|
|
232
|
+
time.sleep(1)
|
|
233
|
+
# print(f"Surveillance des mails toutes les {INTERVAL_SECONDS} secondes. Ctrl+C pour arrêter.")
|
|
234
|
+
# send_mail(
|
|
235
|
+
# expediteur=EMAIL,
|
|
236
|
+
# mot_de_passe=PASSWORD,
|
|
237
|
+
# destinataire="jc@institut-ia.com",
|
|
238
|
+
# sujet="Test depuis Python",
|
|
239
|
+
# contenu="Bonjour,\nVoici un test d'envoi de mail avec Python 2.",
|
|
240
|
+
# piece_jointe_path=r""#C:\Users\max83\Desktop\Orange_4All_AAIT\Orange_4All_AAIT\Orange\Lib\site-packages\Orange\widgets\orangecontrib\HLIT_dev\widgets\icons\input_interface.png" # ou "chemin/vers/fichier.pdf"
|
|
241
|
+
# )
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import json
|
|
3
|
+
import hashlib
|
|
4
|
+
import getpass
|
|
5
|
+
|
|
6
|
+
if "site-packages/Orange/widgets" in os.path.dirname(os.path.abspath(__file__)).replace("\\", "/"):
|
|
7
|
+
from Orange.widgets.orangecontrib.AAIT.utils import MetManagement
|
|
8
|
+
else:
|
|
9
|
+
from orangecontrib.AAIT.utils import MetManagement
|
|
10
|
+
|
|
11
|
+
# Fonction pour générer une clé simple à partir du nom d'utilisateur
|
|
12
|
+
def get_user_key():
|
|
13
|
+
try:
|
|
14
|
+
try:
|
|
15
|
+
username = os.getlogin()
|
|
16
|
+
except OSError:
|
|
17
|
+
username = getpass.getuser()
|
|
18
|
+
|
|
19
|
+
if not username:
|
|
20
|
+
raise ValueError("Nom d'utilisateur introuvable")
|
|
21
|
+
|
|
22
|
+
# On dérive une clé simple (1 octet) depuis le hash du nom d'utilisateur
|
|
23
|
+
digest = hashlib.sha256(username.encode("utf-8")).digest()
|
|
24
|
+
key = digest[0] # 1 octet pour XOR
|
|
25
|
+
return key
|
|
26
|
+
|
|
27
|
+
except Exception as e:
|
|
28
|
+
raise RuntimeError(f"Erreur de génération de clé : {e}")
|
|
29
|
+
|
|
30
|
+
# Fonction simple de chiffrement/déchiffrement par XOR (non sécurisé mais obscurcissant)
|
|
31
|
+
def xor_crypt(data: str, key: int) -> str:
|
|
32
|
+
return ''.join(chr(ord(c) ^ (key & 0xFF)) for c in data)
|
|
33
|
+
|
|
34
|
+
# Fonction pour enregistrer les données dans un fichier JSON avec le mot de passe chiffré
|
|
35
|
+
def enregistrer_config(agent, my_domain, password, interval_second):
|
|
36
|
+
try:
|
|
37
|
+
dossier=MetManagement.get_secret_content_dir()
|
|
38
|
+
# Crée le dossier s'il n'existe pas
|
|
39
|
+
if not os.path.exists(dossier):
|
|
40
|
+
os.makedirs(dossier)
|
|
41
|
+
|
|
42
|
+
# Récupère l'adresse MAC et chiffre le mot de passe
|
|
43
|
+
key = get_user_key()
|
|
44
|
+
mdp_chiffre = xor_crypt(password, key)
|
|
45
|
+
|
|
46
|
+
# Nom du fichier (remplace @ par _at_ pour éviter les problèmes)
|
|
47
|
+
nom_fichier = os.path.join(dossier, f"{agent}{my_domain.replace('@', '_at_')}.json")
|
|
48
|
+
|
|
49
|
+
# Contenu à écrire dans le fichier
|
|
50
|
+
contenu = {
|
|
51
|
+
"agent": agent,
|
|
52
|
+
"domain": my_domain,
|
|
53
|
+
"interval_second": interval_second,
|
|
54
|
+
"password_encrypted": mdp_chiffre
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
# Écriture du fichier
|
|
58
|
+
with open(nom_fichier, "w", encoding="utf-8") as f:
|
|
59
|
+
json.dump(contenu, f, indent=4)
|
|
60
|
+
|
|
61
|
+
print(f"✅ Fichier enregistré : {nom_fichier}")
|
|
62
|
+
return 0
|
|
63
|
+
|
|
64
|
+
except Exception as e:
|
|
65
|
+
print(f"❌ Erreur lors de l'enregistrement : {e}")
|
|
66
|
+
return 1
|
|
67
|
+
|
|
68
|
+
# Fonction pour lire le fichier de configuration et déchiffrer le mot de passe
|
|
69
|
+
def lire_config(chemin_fichier):
|
|
70
|
+
# renvoie une liste =["agent","domain",mdp,"interval_second"]
|
|
71
|
+
try:
|
|
72
|
+
chemin_fichier=MetManagement.get_secret_content_dir()+chemin_fichier
|
|
73
|
+
# Lecture du fichier JSON
|
|
74
|
+
with open(chemin_fichier, "r", encoding="utf-8") as f:
|
|
75
|
+
contenu = json.load(f)
|
|
76
|
+
|
|
77
|
+
# Récupère l'adresse MAC
|
|
78
|
+
key = get_user_key()
|
|
79
|
+
|
|
80
|
+
# Déchiffre le mot de passe
|
|
81
|
+
mdp_dechiffre = xor_crypt(contenu["password_encrypted"], key)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
return [
|
|
85
|
+
contenu["agent"],
|
|
86
|
+
contenu["domain"],
|
|
87
|
+
mdp_dechiffre,
|
|
88
|
+
int(contenu["interval_second"]),
|
|
89
|
+
]
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
except Exception as e:
|
|
93
|
+
print(f"❌ Erreur lors de la lecture : {e}")
|
|
94
|
+
return None
|
|
95
|
+
def enregistrer_config_cli():
|
|
96
|
+
print("\n📝 Écriture d’un fichier de configuration :")
|
|
97
|
+
agent = input("🤖 Nom de l’agent : ").strip()
|
|
98
|
+
domaine = input("📨 @domain.com? : ").strip()
|
|
99
|
+
mdp = input("📨mot de passe? : ").strip()
|
|
100
|
+
interval = int(input("⏱️ Intervalle en secondes : ").strip())
|
|
101
|
+
if 0!= enregistrer_config(agent,domaine,mdp,interval):
|
|
102
|
+
print("erreur!")
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def lire_config_cli():
|
|
106
|
+
chemin_fichier = input("📄 non fichier json (pas le chemin!) JSON : ").strip()
|
|
107
|
+
config = lire_config(chemin_fichier)
|
|
108
|
+
|
|
109
|
+
if config==None:
|
|
110
|
+
print("erreur")
|
|
111
|
+
print(config)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
if __name__ == "__main__":
|
|
116
|
+
print("1) ecrire fichier")
|
|
117
|
+
print("2) dechiffer fichier")
|
|
118
|
+
choix = input("👉 que faire? [1/2] : ").strip()
|
|
119
|
+
|
|
120
|
+
if choix == "1":
|
|
121
|
+
enregistrer_config_cli()
|
|
122
|
+
elif choix == "2":
|
|
123
|
+
lire_config_cli()
|
|
124
|
+
else:
|
|
125
|
+
print("❌ Choix invalide. Réessayez.\n")
|
|
126
|
+
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
# import time
|
|
4
|
+
import requests
|
|
5
|
+
# import orangecanvas.application.canvasmain as canvasmain
|
|
6
|
+
import Orange.data
|
|
7
|
+
from AnyQt.QtWidgets import QApplication, QLabel
|
|
8
|
+
from Orange.widgets import widget # , gui
|
|
9
|
+
from Orange.widgets.utils.signals import Input, Output
|
|
10
|
+
# from Orange.widgets.settings import Setting
|
|
11
|
+
from AnyQt.QtWidgets import QLineEdit, QTextBrowser #, QTextEdit
|
|
12
|
+
|
|
13
|
+
if "site-packages/Orange/widgets" in os.path.dirname(os.path.abspath(__file__)).replace("\\", "/"):
|
|
14
|
+
# from Orange.widgets.orangecontrib.AAIT.llm import answers
|
|
15
|
+
from Orange.widgets.orangecontrib.AAIT.utils import thread_management
|
|
16
|
+
from Orange.widgets.orangecontrib.AAIT.utils.import_uic import uic
|
|
17
|
+
from Orange.widgets.orangecontrib.AAIT.utils.initialize_from_ini import apply_modification_from_python_file
|
|
18
|
+
else:
|
|
19
|
+
# from orangecontrib.AAIT.llm import answers
|
|
20
|
+
from orangecontrib.AAIT.utils import thread_management
|
|
21
|
+
from orangecontrib.AAIT.utils.import_uic import uic
|
|
22
|
+
from orangecontrib.AAIT.utils.initialize_from_ini import apply_modification_from_python_file
|
|
23
|
+
|
|
24
|
+
@apply_modification_from_python_file(filepath_original_widget=__file__)
|
|
25
|
+
class OWDeep_Search(widget.OWWidget):
|
|
26
|
+
name = "Deep Search"
|
|
27
|
+
description = "Generate a response to a column 'prompt' with a LLM"
|
|
28
|
+
icon = "icons/deepsearch.svg"
|
|
29
|
+
if "site-packages/Orange/widgets" in os.path.dirname(os.path.abspath(__file__)).replace("\\", "/"):
|
|
30
|
+
icon = "icons_dev/owqueryllm.svg"
|
|
31
|
+
gui = os.path.join(os.path.dirname(os.path.abspath(__file__)), "designer/owdeepsearch.ui")
|
|
32
|
+
want_control_area = True
|
|
33
|
+
priority = 1089
|
|
34
|
+
|
|
35
|
+
class Inputs:
|
|
36
|
+
api_key = Input("API KEY", Orange.data.Table)
|
|
37
|
+
prompt = Input("Prompt", Orange.data.Table)
|
|
38
|
+
|
|
39
|
+
class Outputs:
|
|
40
|
+
response = Output("Response", Orange.data.Table)
|
|
41
|
+
|
|
42
|
+
@Inputs.api_key
|
|
43
|
+
def set_api_key(self, in_api_key: Orange.data.Table):
|
|
44
|
+
if in_api_key is not None:
|
|
45
|
+
if "api_key" not in in_api_key.domain:
|
|
46
|
+
self.error("You need a column api_key");
|
|
47
|
+
return
|
|
48
|
+
self.api_key = str(in_api_key[0]["api_key"]) # ← cast en str
|
|
49
|
+
self.line_api_key.setText(self.api_key)
|
|
50
|
+
self.error(None)
|
|
51
|
+
if self.autorun:
|
|
52
|
+
self.run()
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@Inputs.prompt
|
|
56
|
+
def set_prompt(self, in_prompt: Orange.data.Table):
|
|
57
|
+
if in_prompt is None:
|
|
58
|
+
self.Outputs.response.send(None)
|
|
59
|
+
return
|
|
60
|
+
if in_prompt is not None:
|
|
61
|
+
if "prompt" not in in_prompt.domain:
|
|
62
|
+
self.error("You need a column prompt");
|
|
63
|
+
return
|
|
64
|
+
self.prompt = str(in_prompt[0]["prompt"]) # ← cast en str
|
|
65
|
+
self.error(None)
|
|
66
|
+
if self.autorun:
|
|
67
|
+
self.run()
|
|
68
|
+
|
|
69
|
+
def __init__(self):
|
|
70
|
+
super().__init__()
|
|
71
|
+
# Qt Managementt
|
|
72
|
+
self.setFixedWidth(700)
|
|
73
|
+
self.setFixedHeight(500)
|
|
74
|
+
uic.loadUi(self.gui, self)
|
|
75
|
+
self.label_description = self.findChild(QLabel, 'Description')
|
|
76
|
+
# print(self.label_description.text())
|
|
77
|
+
self.line_api_key: QLineEdit = self.findChild(QLineEdit, "line_api_key")
|
|
78
|
+
# Connexion du champ à la fonction de mise à jour
|
|
79
|
+
self.line_api_key.editingFinished.connect(self.update_api_key)
|
|
80
|
+
# Text browser
|
|
81
|
+
self.textBrowser = self.findChild(QTextBrowser, 'textBrowser')
|
|
82
|
+
# Data Management
|
|
83
|
+
self.api_key = None
|
|
84
|
+
self.prompt = None
|
|
85
|
+
self.thread = None
|
|
86
|
+
self.autorun = True
|
|
87
|
+
self.can_run = True
|
|
88
|
+
self.result = None # sera ma table de sortie
|
|
89
|
+
# Custom updates
|
|
90
|
+
self.post_initialized()
|
|
91
|
+
|
|
92
|
+
def update_api_key(self):
|
|
93
|
+
self.api_key = self.line_api_key.text().strip()
|
|
94
|
+
|
|
95
|
+
def deepsearch(self, api_key: str, prompt: str):
|
|
96
|
+
"""
|
|
97
|
+
Appelle Perplexity Sonar Deep-Research et renvoie la réponse complète
|
|
98
|
+
dans une Orange Table (métadonnée 'response').
|
|
99
|
+
"""
|
|
100
|
+
|
|
101
|
+
url = "https://api.perplexity.ai/chat/completions"
|
|
102
|
+
headers = {
|
|
103
|
+
"Content-Type": "application/json",
|
|
104
|
+
"Authorization": f"Bearer {api_key}"
|
|
105
|
+
}
|
|
106
|
+
payload = {
|
|
107
|
+
"model": "sonar-pro",
|
|
108
|
+
"messages": [
|
|
109
|
+
{"role": "system",
|
|
110
|
+
"content": ("You are a deep research assistant. "
|
|
111
|
+
"Answer only in Markdown without exposing any chain-of-thought.")},
|
|
112
|
+
{"role": "user", "content": prompt}
|
|
113
|
+
],
|
|
114
|
+
"temperature": 0.0,
|
|
115
|
+
"max_tokens": 20_000,
|
|
116
|
+
"format": "markdown"
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
try:
|
|
120
|
+
resp = requests.post(url, json=payload, headers=headers, timeout=600)
|
|
121
|
+
resp.raise_for_status()
|
|
122
|
+
answer_md = resp.json()["choices"][0]["message"]["content"]
|
|
123
|
+
|
|
124
|
+
except requests.HTTPError: #as e:
|
|
125
|
+
print("STATUS :", resp.status_code) # affiche 400, 401, etc.
|
|
126
|
+
print("DETAIL :", resp.text) # message JSON complet de Perplexity
|
|
127
|
+
|
|
128
|
+
answer_md = f"**Erreur Perplexity :** {resp.text}"
|
|
129
|
+
|
|
130
|
+
except Exception as e:
|
|
131
|
+
answer_md = f"**Erreur Perplexity :** {e}"
|
|
132
|
+
|
|
133
|
+
# Affiche la réponse complète dans la console
|
|
134
|
+
# print("Réponse Perplexity :\n", answer_md)
|
|
135
|
+
|
|
136
|
+
response_var = Orange.data.StringVariable("response")
|
|
137
|
+
|
|
138
|
+
domain = Orange.data.Domain([], metas=[response_var])
|
|
139
|
+
out_data = Orange.data.Table.from_list(domain, [[answer_md]])
|
|
140
|
+
return out_data
|
|
141
|
+
|
|
142
|
+
def run(self):
|
|
143
|
+
# print("I'm running...")
|
|
144
|
+
# print(self.prompt)
|
|
145
|
+
# print(self.api_key)
|
|
146
|
+
if not self.can_run:
|
|
147
|
+
return
|
|
148
|
+
|
|
149
|
+
if self.prompt == None:
|
|
150
|
+
return
|
|
151
|
+
|
|
152
|
+
if self.api_key == None:
|
|
153
|
+
# print("Je sors")
|
|
154
|
+
return
|
|
155
|
+
|
|
156
|
+
# If Thread is already running, interrupt it
|
|
157
|
+
if self.thread is not None:
|
|
158
|
+
if self.thread.isRunning():
|
|
159
|
+
self.thread.safe_quit()
|
|
160
|
+
|
|
161
|
+
# Start progress bar
|
|
162
|
+
self.progressBarInit()
|
|
163
|
+
#◙self.textBrowser.setText("")
|
|
164
|
+
|
|
165
|
+
# Connect and start thread : main function, progress, result and finish
|
|
166
|
+
# --> progress is used in the main function to track progress (with a callback)
|
|
167
|
+
# --> result is used to collect the result from main function
|
|
168
|
+
# --> finish is just an empty signal to indicate that the thread is finished
|
|
169
|
+
self.thread = thread_management.Thread(self.deepsearch, self.api_key, self.prompt)
|
|
170
|
+
self.thread.progress.connect(self.handle_progress)
|
|
171
|
+
self.thread.result.connect(self.handle_result)
|
|
172
|
+
self.thread.finish.connect(self.handle_finish)
|
|
173
|
+
self.thread.start()
|
|
174
|
+
|
|
175
|
+
def handle_progress(self, progress) -> None:
|
|
176
|
+
value = progress[0]
|
|
177
|
+
text = progress[1]
|
|
178
|
+
if value is not None:
|
|
179
|
+
self.progressBarSet(value)
|
|
180
|
+
if text is None:
|
|
181
|
+
self.textBrowser.setText("")
|
|
182
|
+
else:
|
|
183
|
+
self.textBrowser.insertPlainText(text)
|
|
184
|
+
|
|
185
|
+
def handle_result(self, result):
|
|
186
|
+
try:
|
|
187
|
+
self.result = result
|
|
188
|
+
self.Outputs.response.send(result)
|
|
189
|
+
except Exception as e:
|
|
190
|
+
print("An error occurred when sending out_data:", e)
|
|
191
|
+
self.Outputs.response.send(None)
|
|
192
|
+
return
|
|
193
|
+
|
|
194
|
+
def handle_finish(self):
|
|
195
|
+
print("Generation finished")
|
|
196
|
+
self.progressBarFinished()
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def post_initialized(self):
|
|
200
|
+
pass
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
if __name__ == "__main__":
|
|
204
|
+
app = QApplication(sys.argv)
|
|
205
|
+
my_widget = OWDeep_Search()
|
|
206
|
+
my_widget.show()
|
|
207
|
+
app.exec_()
|
|
208
|
+
|