AIEmailAutomationUtility 0.0.30__py3-none-any.whl → 0.0.32__py3-none-any.whl
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.
- AIEmailAutomationUtility/Email_Read.py +64 -3
- AIEmailAutomationUtility/Process_Category.py +29 -11
- {AIEmailAutomationUtility-0.0.30.dist-info → AIEmailAutomationUtility-0.0.32.dist-info}/METADATA +3 -3
- {AIEmailAutomationUtility-0.0.30.dist-info → AIEmailAutomationUtility-0.0.32.dist-info}/RECORD +7 -7
- {AIEmailAutomationUtility-0.0.30.dist-info → AIEmailAutomationUtility-0.0.32.dist-info}/LICENCE.txt +0 -0
- {AIEmailAutomationUtility-0.0.30.dist-info → AIEmailAutomationUtility-0.0.32.dist-info}/WHEEL +0 -0
- {AIEmailAutomationUtility-0.0.30.dist-info → AIEmailAutomationUtility-0.0.32.dist-info}/top_level.txt +0 -0
@@ -10,6 +10,11 @@ import json
|
|
10
10
|
import os
|
11
11
|
import csv
|
12
12
|
|
13
|
+
import io
|
14
|
+
import pandas as pd
|
15
|
+
import docx
|
16
|
+
import PyPDF2
|
17
|
+
|
13
18
|
from .Save_Transaction import Save_Transaction
|
14
19
|
from .Email_Classification import Email_Classification
|
15
20
|
from .EmailReplyAssistant import EmailReplyAssistant
|
@@ -110,7 +115,9 @@ class Email_Read:
|
|
110
115
|
file_JsonArray, categories = self.read_JSON_File(templateName)
|
111
116
|
|
112
117
|
except Exception as e:
|
118
|
+
login_status = "Failed"
|
113
119
|
logger.log(f"Login failed: {e}")
|
120
|
+
raise Exception(e)
|
114
121
|
|
115
122
|
# Log the result
|
116
123
|
self.log_email_login(user_id, reciever_email_addr, Model_Name, login_status)
|
@@ -248,6 +255,7 @@ class Email_Read:
|
|
248
255
|
|
249
256
|
except Exception as e:
|
250
257
|
logger.log(f"Login failed: {e}")
|
258
|
+
return f"Login failed: {e}"
|
251
259
|
|
252
260
|
# Log the result
|
253
261
|
self.log_email_login(user_id, reciever_email_addr, Model_Name, login_status)
|
@@ -294,7 +302,23 @@ class Email_Read:
|
|
294
302
|
|
295
303
|
if content_type == "text/plain":
|
296
304
|
email_body += part.get_payload(decode=True).decode('utf-8', errors='replace')
|
297
|
-
|
305
|
+
|
306
|
+
|
307
|
+
# For attachment
|
308
|
+
if msg.is_multipart():
|
309
|
+
for part in msg.walk():
|
310
|
+
content_disposition = str(part.get("Content-Disposition") or "")
|
311
|
+
content_type = part.get_content_type()
|
312
|
+
if "attachment" in content_disposition.lower():
|
313
|
+
filename = part.get_filename() or "attachment"
|
314
|
+
content_bytes = part.get_payload(decode=True)
|
315
|
+
if content_bytes:
|
316
|
+
extracted_content = self.Extract_attachment_content(filename, content_bytes)
|
317
|
+
extracted_content =f"\n\n--- The content of the attachment '{filename}' is below ---\n{extracted_content}\n"
|
318
|
+
# email_body += f"\n\n--- The content of the attachment '{filename}' is below ---\n{extracted_content}\n"
|
319
|
+
else:
|
320
|
+
extracted_content="NA"
|
321
|
+
|
298
322
|
else:
|
299
323
|
email_body = msg.get_payload(decode=True).decode('utf-8', errors='replace')
|
300
324
|
content_type = msg.get_content_type()
|
@@ -348,7 +372,8 @@ class Email_Read:
|
|
348
372
|
"uid": uid,
|
349
373
|
"to_email_addr": to_email_addr,
|
350
374
|
"user_id": user_id,
|
351
|
-
"is_html": is_html
|
375
|
+
"is_html": is_html,
|
376
|
+
"extracted_content" : extracted_content
|
352
377
|
}
|
353
378
|
processcategory = Process_Category()
|
354
379
|
processcategory.process_cat(emailCategory, dataValues)
|
@@ -554,7 +579,7 @@ class Email_Read:
|
|
554
579
|
|
555
580
|
def log_email_login(self, user_id, email, model_name, login_status):
|
556
581
|
base_dir="EMail_log"
|
557
|
-
timestamp = datetime.now().strftime("%Y-%m-%
|
582
|
+
timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
|
558
583
|
log_dir = os.path.join(base_dir, user_id)
|
559
584
|
os.makedirs(log_dir, exist_ok=True)
|
560
585
|
log_file_path = os.path.join(log_dir, f"{user_id}.csv")
|
@@ -573,5 +598,41 @@ class Email_Read:
|
|
573
598
|
def read_status(self):
|
574
599
|
global shared_status
|
575
600
|
return shared_status
|
601
|
+
|
602
|
+
def Extract_attachment_content(self,filename, content_bytes):
|
603
|
+
extension = os.path.splitext(filename)[1].lower()
|
604
|
+
|
605
|
+
try:
|
606
|
+
if extension == ".pdf":
|
607
|
+
# Extract text from PDF
|
608
|
+
reader = PyPDF2.PdfReader(io.BytesIO(content_bytes))
|
609
|
+
text = "\n".join(page.extract_text() or "" for page in reader.pages)
|
610
|
+
return text if text.strip() else "[No text content found in PDF]"
|
611
|
+
|
612
|
+
elif extension in [".docx", ".doc"]:
|
613
|
+
# Extract text from Word document
|
614
|
+
doc = docx.Document(io.BytesIO(content_bytes))
|
615
|
+
text = "\n".join([para.text for para in doc.paragraphs])
|
616
|
+
return text if text.strip() else "[No text content found in Word document]"
|
617
|
+
|
618
|
+
elif extension in [".xls", ".xlsx"]:
|
619
|
+
# Extract text from Excel sheet
|
620
|
+
df = pd.read_excel(io.BytesIO(content_bytes), engine='openpyxl')
|
621
|
+
return df.to_string(index=False)
|
622
|
+
|
623
|
+
elif extension == ".csv":
|
624
|
+
# Extract text from CSV file
|
625
|
+
df = pd.read_csv(io.BytesIO(content_bytes))
|
626
|
+
return df.to_string(index=False)
|
627
|
+
|
628
|
+
elif extension in [".txt"]:
|
629
|
+
# Decode plain text file
|
630
|
+
return content_bytes.decode('utf-8', errors='replace')
|
631
|
+
|
632
|
+
else:
|
633
|
+
return "[Unsupported attachment type]"
|
634
|
+
|
635
|
+
except Exception as e:
|
636
|
+
return f"[Error extracting content: {str(e)}]"
|
576
637
|
|
577
638
|
|
@@ -106,12 +106,13 @@ class Process_Category:
|
|
106
106
|
logger.log(f"document_type 108::: {document_type}")
|
107
107
|
|
108
108
|
if responseMethod == "Upload_Document":
|
109
|
-
# Get today's date folder path
|
110
|
-
today_date = datetime.today().strftime('%Y-%m-%d')
|
111
|
-
order_folder = os.path.join(document_type, today_date)
|
112
109
|
|
113
110
|
email_upload_document = Email_DocumentUploader()
|
114
111
|
if len(fileName) != 0 and document_type != '':
|
112
|
+
# Get today's date folder path
|
113
|
+
today_date = datetime.today().strftime('%Y-%m-%d')
|
114
|
+
order_folder = os.path.join(document_type, today_date)
|
115
|
+
|
115
116
|
file_path = os.path.join(order_folder, fileName) # Correct file path
|
116
117
|
|
117
118
|
with open(file_path, "rb") as file:
|
@@ -161,8 +162,17 @@ class Process_Category:
|
|
161
162
|
# Extract customer code once
|
162
163
|
customer_code = customer_data.get("customer_code", "")
|
163
164
|
|
165
|
+
|
164
166
|
# Step 5: Identify product from email using AI
|
165
|
-
|
167
|
+
|
168
|
+
#If there is attachment then append the content in the body.
|
169
|
+
extracted_content = dataValues.get('extracted_content')
|
170
|
+
if extracted_content != "NA":
|
171
|
+
attachment_email_body =email_body + " \n\n" + extracted_content
|
172
|
+
products = self.identify_products(attachment_email_body, subject, Model_Name, openai_api_key, geminiAI_APIKey, localAIURL, parameters["Product_Assistant_Id"])
|
173
|
+
else:
|
174
|
+
products = self.identify_products(email_body, subject, Model_Name, openai_api_key, geminiAI_APIKey, localAIURL, parameters["Product_Assistant_Id"])
|
175
|
+
|
166
176
|
logger.log(f'Identified Products are ::: {products}')
|
167
177
|
|
168
178
|
product_determination=products
|
@@ -207,6 +217,8 @@ class Process_Category:
|
|
207
217
|
else:
|
208
218
|
logger.log(f"Process_Category - Quotation [0] No base price found for item '{item_no}' .")
|
209
219
|
product["rate"] = "NA"
|
220
|
+
product["Price Pickup Source"]="NA"
|
221
|
+
product["Discount"]="NA"
|
210
222
|
continue
|
211
223
|
|
212
224
|
# Condition 1: Exact match in special_rate_customer_wise
|
@@ -226,6 +238,7 @@ class Process_Category:
|
|
226
238
|
found_rate = True
|
227
239
|
logger.log(f"Process_Category - Quotation [1] Special Rate for item '{item_no}' and customer '{customer_code}' is {rate}")
|
228
240
|
price_pickup_source="SPECIAL_RATE_CUSTOMER_WISE"
|
241
|
+
Discount="NA"
|
229
242
|
|
230
243
|
# Condition 2: Customer + Manufacturer discount
|
231
244
|
if not found_rate:
|
@@ -241,6 +254,7 @@ class Process_Category:
|
|
241
254
|
|
242
255
|
if discount_result:
|
243
256
|
discount_percent = discount_result[0]
|
257
|
+
Discount= discount_percent
|
244
258
|
rate = raw_price * (1 - int(discount_percent) / 100)
|
245
259
|
rate = round(rate, 2)
|
246
260
|
found_rate = True
|
@@ -265,6 +279,7 @@ class Process_Category:
|
|
265
279
|
|
266
280
|
if past_sales_result:
|
267
281
|
most_common_make, past_discount = past_sales_result
|
282
|
+
Discount=past_discount
|
268
283
|
if isinstance(raw_price, (int, float)):
|
269
284
|
rate = raw_price * (1 - int(past_discount) / 100)
|
270
285
|
rate = round(rate, 2)
|
@@ -288,6 +303,7 @@ class Process_Category:
|
|
288
303
|
|
289
304
|
if general_discount_result:
|
290
305
|
general_discount_percent = general_discount_result[0]
|
306
|
+
Discount=general_discount_percent
|
291
307
|
rate = raw_price * (1 - int(general_discount_percent) / 100)
|
292
308
|
rate = round(rate, 2)
|
293
309
|
found_rate = True
|
@@ -299,9 +315,11 @@ class Process_Category:
|
|
299
315
|
rate = raw_price
|
300
316
|
logger.log(f"Process_Category - Quotation [5] No discounts applied. Using base price for item '{item_no}': {rate}")
|
301
317
|
price_pickup_source="PRICE_LIST"
|
318
|
+
Discount = "NA"
|
302
319
|
|
303
320
|
product["rate"] = rate
|
304
321
|
product["Price Pickup Source"]=price_pickup_source
|
322
|
+
product["Discount"]=Discount
|
305
323
|
|
306
324
|
db_connection.close()
|
307
325
|
|
@@ -349,7 +367,7 @@ class Process_Category:
|
|
349
367
|
mail.expunge()
|
350
368
|
logger.log(f"Mail removed from inbox and added to '{LABEL}' label.")
|
351
369
|
|
352
|
-
current_timestamp = datetime.now().strftime("%Y-%m-%
|
370
|
+
current_timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
|
353
371
|
filename = f'{uid}{current_timestamp}'
|
354
372
|
logger.log(f"The file name for csv and eml is ::: {filename}")
|
355
373
|
self.store_email_details_to_csv(
|
@@ -409,7 +427,7 @@ class Process_Category:
|
|
409
427
|
filename = part.get_filename()
|
410
428
|
mime_type = part.get_content_type().lower()
|
411
429
|
|
412
|
-
if "pdf" in mime_type:
|
430
|
+
if "pdf" in mime_type or filename.lower().endswith(".pdf"):
|
413
431
|
document_type = "Orders"
|
414
432
|
elif "msword" in mime_type or "wordprocessingml" in mime_type or filename.lower().endswith(".docx"):
|
415
433
|
document_type = "Order Excel"
|
@@ -421,12 +439,12 @@ class Process_Category:
|
|
421
439
|
document_type = "Order Email"
|
422
440
|
elif "rtf" in mime_type or filename.lower().endswith(".rtf"):
|
423
441
|
document_type = "Order Excel"
|
424
|
-
|
425
|
-
today_date = datetime.today().strftime('%Y-%m-%d') # Format: YYYY-MM-DD
|
426
|
-
date_folder = os.path.join(document_type, today_date) # Path: ORDERS/YYYY-MM-DD
|
427
|
-
os.makedirs(date_folder, exist_ok=True)
|
428
442
|
|
429
443
|
if filename:
|
444
|
+
today_date = datetime.today().strftime('%Y-%m-%d') # Format: YYYY-MM-DD
|
445
|
+
date_folder = os.path.join(document_type, today_date) # Path: ORDERS/YYYY-MM-DD
|
446
|
+
os.makedirs(date_folder, exist_ok=True)
|
447
|
+
|
430
448
|
filepath = os.path.join(date_folder, filename) # Save inside date-wise folder
|
431
449
|
|
432
450
|
with open(filepath, 'wb') as f:
|
@@ -462,7 +480,7 @@ class Process_Category:
|
|
462
480
|
fileName = fileName + ".txt"
|
463
481
|
filePath = os.path.join(order_folder, fileName)
|
464
482
|
|
465
|
-
with open(filePath, "w") as file:
|
483
|
+
with open(filePath, "w", encoding="utf-8") as file:
|
466
484
|
file.write(text)
|
467
485
|
logger.log(f"New TXT file created from email body and stored in '{filePath}'")
|
468
486
|
else:
|
{AIEmailAutomationUtility-0.0.30.dist-info → AIEmailAutomationUtility-0.0.32.dist-info}/METADATA
RENAMED
@@ -1,7 +1,7 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: AIEmailAutomationUtility
|
3
|
-
Version: 0.0.
|
4
|
-
Summary:
|
3
|
+
Version: 0.0.32
|
4
|
+
Summary: Make changes in code, should run for windows and ubuntu
|
5
5
|
Author: Proteus Technology PVT. LTD.
|
6
6
|
Author-email: <apps@baseinformation.com>
|
7
7
|
Keywords: python,first package
|
@@ -13,4 +13,4 @@ Classifier: Operating System :: MacOS :: MacOS X
|
|
13
13
|
Classifier: Operating System :: Microsoft :: Windows
|
14
14
|
License-File: LICENCE.txt
|
15
15
|
|
16
|
-
|
16
|
+
Make changes in code, should run for windows and ubuntu
|
{AIEmailAutomationUtility-0.0.30.dist-info → AIEmailAutomationUtility-0.0.32.dist-info}/RECORD
RENAMED
@@ -2,14 +2,14 @@ AIEmailAutomationUtility/EmailReplyAssistant.py,sha256=R_wJna3-ITsVxQEccryhM93T_
|
|
2
2
|
AIEmailAutomationUtility/Email_Classification.py,sha256=Ar0g4Ff8HOT7xICktd3nP_C_vCyeY-xCpUjVCVRWAyc,9417
|
3
3
|
AIEmailAutomationUtility/Email_DocumentUploader.py,sha256=YJu4tuTHr0K-5vuds9gZfj-Hwsgm4MuAOP39Lmu_t98,3219
|
4
4
|
AIEmailAutomationUtility/Email_Draft.py,sha256=DcyBeDaE8CReKHnHxLiz-o2tDxuUgwy91c4k0qhQbVw,7749
|
5
|
-
AIEmailAutomationUtility/Email_Read.py,sha256=
|
5
|
+
AIEmailAutomationUtility/Email_Read.py,sha256=zE2dK4h0XfKWuSuMJs3E2U-yMPHzRtBfNSdP74h_pDI,30743
|
6
6
|
AIEmailAutomationUtility/Email_Upload_Document.py,sha256=3bdkxfDlwoeRp-46KPw2Gs1dqBhEIoA1yE5GCudpdV8,1320
|
7
|
-
AIEmailAutomationUtility/Process_Category.py,sha256=
|
7
|
+
AIEmailAutomationUtility/Process_Category.py,sha256=xXiXYf_dmscKO6scFOMRCWLgKhsV8RsCVK1AXPnE1DI,44695
|
8
8
|
AIEmailAutomationUtility/Save_Draft.py,sha256=yzLgFN14I_lXE6qL0I3tKNduvcnWdbsY9i2mKdTtio4,5348
|
9
9
|
AIEmailAutomationUtility/Save_Transaction.py,sha256=Gg1w6hhzHmEFjsuzYvkq-3-EsWReetjLHsYSv5YIGgM,3816
|
10
10
|
AIEmailAutomationUtility/__init__.py,sha256=Jad3IdPRsVMeLqEEh-FbCrc1lE2tzJO2DTG5Hgmxh5g,391
|
11
|
-
AIEmailAutomationUtility-0.0.
|
12
|
-
AIEmailAutomationUtility-0.0.
|
13
|
-
AIEmailAutomationUtility-0.0.
|
14
|
-
AIEmailAutomationUtility-0.0.
|
15
|
-
AIEmailAutomationUtility-0.0.
|
11
|
+
AIEmailAutomationUtility-0.0.32.dist-info/LICENCE.txt,sha256=2qX9IkEUBx0VJp1Vh9O2dsRwE-IpYId0lXDyn7OVsJ8,1073
|
12
|
+
AIEmailAutomationUtility-0.0.32.dist-info/METADATA,sha256=m0b6nXm2W1DSYwxU9Mbt8oPd7ujQ90BDc8I9ohdK3ck,611
|
13
|
+
AIEmailAutomationUtility-0.0.32.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
14
|
+
AIEmailAutomationUtility-0.0.32.dist-info/top_level.txt,sha256=3jTWrTUblVkaP7mpwY2UBSnrlfot5Ykpfsehyke-Uzw,25
|
15
|
+
AIEmailAutomationUtility-0.0.32.dist-info/RECORD,,
|
{AIEmailAutomationUtility-0.0.30.dist-info → AIEmailAutomationUtility-0.0.32.dist-info}/LICENCE.txt
RENAMED
File without changes
|
{AIEmailAutomationUtility-0.0.30.dist-info → AIEmailAutomationUtility-0.0.32.dist-info}/WHEEL
RENAMED
File without changes
|
File without changes
|