AIEmailAutomationUtility 0.0.29__py3-none-any.whl → 0.0.31__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 +63 -2
- AIEmailAutomationUtility/Process_Category.py +19 -2
- {AIEmailAutomationUtility-0.0.29.dist-info → AIEmailAutomationUtility-0.0.31.dist-info}/METADATA +3 -3
- {AIEmailAutomationUtility-0.0.29.dist-info → AIEmailAutomationUtility-0.0.31.dist-info}/RECORD +7 -7
- {AIEmailAutomationUtility-0.0.29.dist-info → AIEmailAutomationUtility-0.0.31.dist-info}/LICENCE.txt +0 -0
- {AIEmailAutomationUtility-0.0.29.dist-info → AIEmailAutomationUtility-0.0.31.dist-info}/WHEEL +0 -0
- {AIEmailAutomationUtility-0.0.29.dist-info → AIEmailAutomationUtility-0.0.31.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)
|
@@ -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
|
|
@@ -161,13 +161,22 @@ class Process_Category:
|
|
161
161
|
# Extract customer code once
|
162
162
|
customer_code = customer_data.get("customer_code", "")
|
163
163
|
|
164
|
+
|
164
165
|
# Step 5: Identify product from email using AI
|
165
|
-
|
166
|
+
|
167
|
+
#If there is attachment then append the content in the body.
|
168
|
+
extracted_content = dataValues.get('extracted_content')
|
169
|
+
if extracted_content != "NA":
|
170
|
+
attachment_email_body =email_body + " \n\n" + extracted_content
|
171
|
+
products = self.identify_products(attachment_email_body, subject, Model_Name, openai_api_key, geminiAI_APIKey, localAIURL, parameters["Product_Assistant_Id"])
|
172
|
+
else:
|
173
|
+
products = self.identify_products(email_body, subject, Model_Name, openai_api_key, geminiAI_APIKey, localAIURL, parameters["Product_Assistant_Id"])
|
174
|
+
|
166
175
|
logger.log(f'Identified Products are ::: {products}')
|
167
176
|
|
168
177
|
product_determination=products
|
169
178
|
|
170
|
-
db_connection = sqlite3.connect('
|
179
|
+
db_connection = sqlite3.connect('database/fetchprice.db')
|
171
180
|
for product in products:
|
172
181
|
item_no = product.get("item_no", "").strip()
|
173
182
|
make = product.get("make", "").strip()
|
@@ -207,6 +216,8 @@ class Process_Category:
|
|
207
216
|
else:
|
208
217
|
logger.log(f"Process_Category - Quotation [0] No base price found for item '{item_no}' .")
|
209
218
|
product["rate"] = "NA"
|
219
|
+
product["Price Pickup Source"]="NA"
|
220
|
+
product["Discount"]="NA"
|
210
221
|
continue
|
211
222
|
|
212
223
|
# Condition 1: Exact match in special_rate_customer_wise
|
@@ -226,6 +237,7 @@ class Process_Category:
|
|
226
237
|
found_rate = True
|
227
238
|
logger.log(f"Process_Category - Quotation [1] Special Rate for item '{item_no}' and customer '{customer_code}' is {rate}")
|
228
239
|
price_pickup_source="SPECIAL_RATE_CUSTOMER_WISE"
|
240
|
+
Discount="NA"
|
229
241
|
|
230
242
|
# Condition 2: Customer + Manufacturer discount
|
231
243
|
if not found_rate:
|
@@ -241,6 +253,7 @@ class Process_Category:
|
|
241
253
|
|
242
254
|
if discount_result:
|
243
255
|
discount_percent = discount_result[0]
|
256
|
+
Discount= discount_percent
|
244
257
|
rate = raw_price * (1 - int(discount_percent) / 100)
|
245
258
|
rate = round(rate, 2)
|
246
259
|
found_rate = True
|
@@ -265,6 +278,7 @@ class Process_Category:
|
|
265
278
|
|
266
279
|
if past_sales_result:
|
267
280
|
most_common_make, past_discount = past_sales_result
|
281
|
+
Discount=past_discount
|
268
282
|
if isinstance(raw_price, (int, float)):
|
269
283
|
rate = raw_price * (1 - int(past_discount) / 100)
|
270
284
|
rate = round(rate, 2)
|
@@ -288,6 +302,7 @@ class Process_Category:
|
|
288
302
|
|
289
303
|
if general_discount_result:
|
290
304
|
general_discount_percent = general_discount_result[0]
|
305
|
+
Discount=general_discount_percent
|
291
306
|
rate = raw_price * (1 - int(general_discount_percent) / 100)
|
292
307
|
rate = round(rate, 2)
|
293
308
|
found_rate = True
|
@@ -299,9 +314,11 @@ class Process_Category:
|
|
299
314
|
rate = raw_price
|
300
315
|
logger.log(f"Process_Category - Quotation [5] No discounts applied. Using base price for item '{item_no}': {rate}")
|
301
316
|
price_pickup_source="PRICE_LIST"
|
317
|
+
Discount = "NA"
|
302
318
|
|
303
319
|
product["rate"] = rate
|
304
320
|
product["Price Pickup Source"]=price_pickup_source
|
321
|
+
product["Discount"]=Discount
|
305
322
|
|
306
323
|
db_connection.close()
|
307
324
|
|
{AIEmailAutomationUtility-0.0.29.dist-info → AIEmailAutomationUtility-0.0.31.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.31
|
4
|
+
Summary: If attachement in email download it and pass attachement file data to product identification service
|
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
|
+
If attachement in email download it and pass attachement file data to product identification service
|
{AIEmailAutomationUtility-0.0.29.dist-info → AIEmailAutomationUtility-0.0.31.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=6obif-ZLhxuatNcGhO8en_R7-Q56EOhzeyILb8vZBjM,30743
|
6
6
|
AIEmailAutomationUtility/Email_Upload_Document.py,sha256=3bdkxfDlwoeRp-46KPw2Gs1dqBhEIoA1yE5GCudpdV8,1320
|
7
|
-
AIEmailAutomationUtility/Process_Category.py,sha256=
|
7
|
+
AIEmailAutomationUtility/Process_Category.py,sha256=Rhs_flyv-_LvDzu6aO_i-ZDjnMKWDIP_mAcqf8mkl_Y,44607
|
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.31.dist-info/LICENCE.txt,sha256=2qX9IkEUBx0VJp1Vh9O2dsRwE-IpYId0lXDyn7OVsJ8,1073
|
12
|
+
AIEmailAutomationUtility-0.0.31.dist-info/METADATA,sha256=E_KUzOjVXHaejQTsqUsfCZqLQYTWJQIFY4xUrZufw_o,701
|
13
|
+
AIEmailAutomationUtility-0.0.31.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
14
|
+
AIEmailAutomationUtility-0.0.31.dist-info/top_level.txt,sha256=3jTWrTUblVkaP7mpwY2UBSnrlfot5Ykpfsehyke-Uzw,25
|
15
|
+
AIEmailAutomationUtility-0.0.31.dist-info/RECORD,,
|
{AIEmailAutomationUtility-0.0.29.dist-info → AIEmailAutomationUtility-0.0.31.dist-info}/LICENCE.txt
RENAMED
File without changes
|
{AIEmailAutomationUtility-0.0.29.dist-info → AIEmailAutomationUtility-0.0.31.dist-info}/WHEEL
RENAMED
File without changes
|
File without changes
|