AIEmailAutomationUtility 0.0.32__py3-none-any.whl → 0.0.33__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_DocumentUploader.py +0 -1
- AIEmailAutomationUtility/Email_Draft.py +1 -0
- AIEmailAutomationUtility/Email_Read.py +23 -10
- AIEmailAutomationUtility/Process_Category.py +73 -9
- {AIEmailAutomationUtility-0.0.32.dist-info → AIEmailAutomationUtility-0.0.33.dist-info}/METADATA +3 -3
- AIEmailAutomationUtility-0.0.33.dist-info/RECORD +15 -0
- AIEmailAutomationUtility-0.0.32.dist-info/RECORD +0 -15
- {AIEmailAutomationUtility-0.0.32.dist-info → AIEmailAutomationUtility-0.0.33.dist-info}/LICENCE.txt +0 -0
- {AIEmailAutomationUtility-0.0.32.dist-info → AIEmailAutomationUtility-0.0.33.dist-info}/WHEEL +0 -0
- {AIEmailAutomationUtility-0.0.32.dist-info → AIEmailAutomationUtility-0.0.33.dist-info}/top_level.txt +0 -0
@@ -7,7 +7,7 @@ from datetime import datetime
|
|
7
7
|
import threading
|
8
8
|
import traceback
|
9
9
|
import json
|
10
|
-
import os
|
10
|
+
import os, shutil
|
11
11
|
import csv
|
12
12
|
|
13
13
|
import io
|
@@ -112,7 +112,7 @@ class Email_Read:
|
|
112
112
|
login_status = "Success"
|
113
113
|
mail.select('inbox')
|
114
114
|
|
115
|
-
file_JsonArray, categories = self.read_JSON_File(templateName)
|
115
|
+
file_JsonArray, categories = self.read_JSON_File(templateName, user_id)
|
116
116
|
|
117
117
|
except Exception as e:
|
118
118
|
login_status = "Failed"
|
@@ -251,7 +251,7 @@ class Email_Read:
|
|
251
251
|
login_status = "Success"
|
252
252
|
mail.select('inbox')
|
253
253
|
|
254
|
-
file_JsonArray, categories = self.read_JSON_File(templateName)
|
254
|
+
file_JsonArray, categories = self.read_JSON_File(templateName, user_id)
|
255
255
|
|
256
256
|
except Exception as e:
|
257
257
|
logger.log(f"Login failed: {e}")
|
@@ -484,7 +484,7 @@ class Email_Read:
|
|
484
484
|
templateName = "ai_email_automation.json"
|
485
485
|
fileName = ""
|
486
486
|
|
487
|
-
file_JsonArray, categories = self.read_JSON_File(templateName)
|
487
|
+
file_JsonArray, categories = self.read_JSON_File(templateName, user_id)
|
488
488
|
# Call the `extract_all_email_info` method to extract details from the eml content
|
489
489
|
extracted_info,msg = self.extract_all_email_info(eml_content)
|
490
490
|
|
@@ -552,24 +552,37 @@ class Email_Read:
|
|
552
552
|
|
553
553
|
return "success"
|
554
554
|
|
555
|
-
def read_JSON_File(self, json_fileName):
|
555
|
+
def read_JSON_File(self, json_fileName, user_id):
|
556
556
|
category_list = []
|
557
557
|
categories = ""
|
558
558
|
try:
|
559
|
-
|
560
|
-
|
561
|
-
|
559
|
+
logger.log(f"\nEmail_Read() read_JSON_File user_id ::: {user_id}")
|
560
|
+
user_file = json_fileName
|
561
|
+
if user_id:
|
562
|
+
user_dir = os.path.join('user_data', user_id)
|
563
|
+
logger.log(f"\nEmail_Read() read_JSON_File user_dir ::: {user_dir}")
|
564
|
+
if not os.path.exists(user_dir):
|
565
|
+
os.makedirs(user_dir, exist_ok=True)
|
566
|
+
user_file = os.path.join(user_dir, json_fileName)
|
567
|
+
if not os.path.exists(user_file) and os.path.exists(json_fileName):
|
568
|
+
shutil.copy(json_fileName, user_file)
|
569
|
+
|
570
|
+
logger.log(f"\nEmail_Read() read_JSON_File user_file ::: {user_file}")
|
571
|
+
|
572
|
+
if os.path.exists(user_file):
|
573
|
+
with open(user_file, "r") as fileObj:
|
574
|
+
file_JsonArray = json.load(fileObj)
|
562
575
|
|
563
576
|
for eachJson in file_JsonArray :
|
564
577
|
for key, value in eachJson.items():
|
565
|
-
if key == "Category"
|
578
|
+
if key == "Category":
|
566
579
|
category_list.append(value)
|
567
580
|
# categories = ", ".join(category_list)
|
568
581
|
|
569
582
|
return file_JsonArray, category_list
|
570
583
|
|
571
584
|
else:
|
572
|
-
message = f"{
|
585
|
+
message = f"{user_file} file not found."
|
573
586
|
raise Exception(message)
|
574
587
|
except Exception as e:
|
575
588
|
msg = f"'{json_fileName}' file is empty. Please provide JSON parameters in the filename."
|
@@ -14,6 +14,8 @@ import openai
|
|
14
14
|
import json
|
15
15
|
import csv
|
16
16
|
import os
|
17
|
+
from email import message_from_string
|
18
|
+
import re
|
17
19
|
|
18
20
|
class Process_Category:
|
19
21
|
|
@@ -129,7 +131,22 @@ class Process_Category:
|
|
129
131
|
|
130
132
|
parameters["DOCUMENT_TYPE"] = document_type
|
131
133
|
logger.log(f"Updated Parameters ::: {parameters}")
|
132
|
-
|
134
|
+
|
135
|
+
email_parts = []
|
136
|
+
if sender_email_addr:
|
137
|
+
email_parts.append(f"From: {sender_email_addr}")
|
138
|
+
if to_email_addr:
|
139
|
+
email_parts.append(f"To: {to_email_addr}")
|
140
|
+
if cc_email_addr:
|
141
|
+
email_parts.append(f"CC: {cc_email_addr}")
|
142
|
+
if subject:
|
143
|
+
email_parts.append(f"Subject: {subject}")
|
144
|
+
|
145
|
+
email_parts.append(email_body)
|
146
|
+
email_body_with_details = "\n".join(email_parts)
|
147
|
+
|
148
|
+
logger.log(f"email_body ::: {email_body}")
|
149
|
+
new_fileName = self.create_file_from_emailBody(email_body_with_details, sender_email_addr, parameters)
|
133
150
|
new_file_path = os.path.join(order_folder, new_fileName)
|
134
151
|
|
135
152
|
with open(new_file_path, "rb") as file:
|
@@ -162,12 +179,11 @@ class Process_Category:
|
|
162
179
|
# Extract customer code once
|
163
180
|
customer_code = customer_data.get("customer_code", "")
|
164
181
|
|
165
|
-
|
166
|
-
# Step 5: Identify product from email using AI
|
167
|
-
|
182
|
+
# Step 5: Identify product from email using AI
|
168
183
|
#If there is attachment then append the content in the body.
|
169
184
|
extracted_content = dataValues.get('extracted_content')
|
170
|
-
|
185
|
+
logger.log(f"extracted_content is ::: {extracted_content}")
|
186
|
+
if extracted_content != "NA" and extracted_content != None:
|
171
187
|
attachment_email_body =email_body + " \n\n" + extracted_content
|
172
188
|
products = self.identify_products(attachment_email_body, subject, Model_Name, openai_api_key, geminiAI_APIKey, localAIURL, parameters["Product_Assistant_Id"])
|
173
189
|
else:
|
@@ -526,7 +542,7 @@ class Process_Category:
|
|
526
542
|
logger.log("Inside identify_products")
|
527
543
|
|
528
544
|
if model_type == "OpenAI":
|
529
|
-
prompt = f"""/* Extract complete item pricing information from the following mixed-format email content. The email may contain a combination of:- descriptive product listings- tabular structured price info- semi-structured lines with HSN, quantity, material, etc. Give me price in INR and information of all items in following format requested_description, item_no, make, description, price, price unit, inventory unit strictly in JSON format. If the item is not available in the assistant files return requested description from email data and rest of the columns as NA. Do not include any instruction as the output will be directly in a program. */ /n {email_body}. """
|
545
|
+
prompt = f"""/* Extract complete item pricing information from the following mixed-format email content. The email may contain a combination of:- descriptive product listings- tabular structured price info- semi-structured lines with HSN, quantity, material, etc. Give me price in INR and information of all items in following format requested_description, item_no, make, description, price, Quantity, price unit, inventory unit strictly in JSON format. If the item is not available in the assistant files return requested description from email data and rest of the columns as NA. Do not include any instruction as the output will be directly in a program. */ /n {email_body}. """
|
530
546
|
emailreplyassistant = EmailReplyAssistant()
|
531
547
|
ai_result = emailreplyassistant.identify_customer_product_reply_assitant(openai_api_key, assistant_id, email_body, subject, prompt)
|
532
548
|
|
@@ -555,13 +571,31 @@ class Process_Category:
|
|
555
571
|
return product_data
|
556
572
|
|
557
573
|
def generate_quotation_draft(self, customer_data, products, model_type, openai_api_key, gemini_api_key, local_ai_url, assistant_id, email_body, subject, signature):
|
558
|
-
logger.log("Inside generate_quotation_draft")
|
559
574
|
|
560
575
|
customer = customer_data
|
561
576
|
|
562
577
|
product_table = "Products:\n"
|
563
578
|
for product in products:
|
564
|
-
|
579
|
+
rate = product.get("rate", "NA")
|
580
|
+
quantity = product.get("Quantity", "NA")
|
581
|
+
try:
|
582
|
+
total = float(rate) * float(quantity)
|
583
|
+
except:
|
584
|
+
total = rate if rate != "NA" and rate not in ("", None) else "-"
|
585
|
+
product_table += (
|
586
|
+
f'- Requested Description: {product.get("requested_description", "-")}, '
|
587
|
+
f'Item Code: {product.get("item_no", "-")}, '
|
588
|
+
f'Item Description: {product.get("description", "-")}, '
|
589
|
+
f'Manufacturer: {product.get("make", "-")}, '
|
590
|
+
f'Price: {product.get("price", "-")}, '
|
591
|
+
f'Inventory Unit: {product.get("inventory unit", "-")}, '
|
592
|
+
f'Discount: {product.get("Discount", "-")}, '
|
593
|
+
f'Rate: {rate}, '
|
594
|
+
f'Quantity: {quantity}, '
|
595
|
+
f'Total: {total}, '
|
596
|
+
f'Price Pickup Source: {product.get("Price Pickup Source", "-")}, '
|
597
|
+
f'Availability: ""\n'
|
598
|
+
)
|
565
599
|
|
566
600
|
if model_type == "OpenAI":
|
567
601
|
prompt = f"""
|
@@ -571,7 +605,12 @@ class Process_Category:
|
|
571
605
|
Customer Code: {customer.get('customer_code', '')}
|
572
606
|
|
573
607
|
{product_table}
|
574
|
-
|
608
|
+
Ensure the table has the following columns in this exact order:
|
609
|
+
Sr. No., Requested Description, Item Code, Item Description, Manufacturer, Price, Inventory Unit, Discount, Rate, Quantity, Total, Price Pickup Source, Availability
|
610
|
+
|
611
|
+
- If any value is missing, use a dash ("-") instead.
|
612
|
+
- "Total" is calculated as Rate × Quantity.
|
613
|
+
- "Availability" should be a blank column.
|
575
614
|
Original Email Subject: {subject}
|
576
615
|
|
577
616
|
Return only the following JSON String format:
|
@@ -788,6 +827,17 @@ class Process_Category:
|
|
788
827
|
with the name user_id.csv.
|
789
828
|
"""
|
790
829
|
|
830
|
+
logger.log(f"The response am getting in csv is : {response}")
|
831
|
+
match = re.search(
|
832
|
+
r'Content-Transfer-Encoding:\s*base64\s+([\s\S]+?)\n--',
|
833
|
+
response,
|
834
|
+
re.IGNORECASE
|
835
|
+
)
|
836
|
+
if match:
|
837
|
+
response = self.extract_html_from_mime(response)
|
838
|
+
|
839
|
+
logger.log(f"The response after am getting in csv is : {response}")
|
840
|
+
|
791
841
|
# Ensure the Mail_log folder exists
|
792
842
|
log_folder = "Mail_log"
|
793
843
|
os.makedirs(log_folder, exist_ok=True)
|
@@ -911,4 +961,18 @@ class Process_Category:
|
|
911
961
|
if user_id:
|
912
962
|
return user_id.value
|
913
963
|
return None
|
964
|
+
|
965
|
+
def extract_html_from_mime(self, raw_data):
|
966
|
+
msg = message_from_string(raw_data)
|
967
|
+
|
968
|
+
if msg.is_multipart():
|
969
|
+
for part in msg.walk():
|
970
|
+
content_type = part.get_content_type()
|
971
|
+
content_encoding = part.get("Content-Transfer-Encoding", "").lower()
|
972
|
+
|
973
|
+
# Look for text/html part with base64 encoding
|
974
|
+
if content_type == "text/html" and content_encoding == "base64":
|
975
|
+
payload = part.get_payload(decode=True)
|
976
|
+
return payload.decode(part.get_content_charset() or 'utf-8')
|
977
|
+
return "No HTML content found."
|
914
978
|
|
{AIEmailAutomationUtility-0.0.32.dist-info → AIEmailAutomationUtility-0.0.33.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.33
|
4
|
+
Summary: Added new columns and Modified the draft prompt to return draft in consistent format for all mails
|
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
|
+
Added new columns and Modified the draft prompt to return draft in consistent format for all mails
|
@@ -0,0 +1,15 @@
|
|
1
|
+
AIEmailAutomationUtility/EmailReplyAssistant.py,sha256=R_wJna3-ITsVxQEccryhM93T_Nf_Oxo8DXnS-sDN8VE,6679
|
2
|
+
AIEmailAutomationUtility/Email_Classification.py,sha256=Ar0g4Ff8HOT7xICktd3nP_C_vCyeY-xCpUjVCVRWAyc,9417
|
3
|
+
AIEmailAutomationUtility/Email_DocumentUploader.py,sha256=BWNRt2X-E2HCogBaKDfl7cZZNSkZUeIsVs8iXjFjH88,3218
|
4
|
+
AIEmailAutomationUtility/Email_Draft.py,sha256=JYZijUh_zan2asyMYQwIBwIpGNJ5SSQGma5AL1meaXk,7808
|
5
|
+
AIEmailAutomationUtility/Email_Read.py,sha256=Ehzx9SKjzCgWY9K4rWCnwokDzo6M0Nbhi2XLKYpeEis,31451
|
6
|
+
AIEmailAutomationUtility/Email_Upload_Document.py,sha256=3bdkxfDlwoeRp-46KPw2Gs1dqBhEIoA1yE5GCudpdV8,1320
|
7
|
+
AIEmailAutomationUtility/Process_Category.py,sha256=9X3b2BEw31T6RmJ66g0zMP8WYVlVqi8CDGMIsR6ATbU,47741
|
8
|
+
AIEmailAutomationUtility/Save_Draft.py,sha256=yzLgFN14I_lXE6qL0I3tKNduvcnWdbsY9i2mKdTtio4,5348
|
9
|
+
AIEmailAutomationUtility/Save_Transaction.py,sha256=Gg1w6hhzHmEFjsuzYvkq-3-EsWReetjLHsYSv5YIGgM,3816
|
10
|
+
AIEmailAutomationUtility/__init__.py,sha256=Jad3IdPRsVMeLqEEh-FbCrc1lE2tzJO2DTG5Hgmxh5g,391
|
11
|
+
AIEmailAutomationUtility-0.0.33.dist-info/LICENCE.txt,sha256=2qX9IkEUBx0VJp1Vh9O2dsRwE-IpYId0lXDyn7OVsJ8,1073
|
12
|
+
AIEmailAutomationUtility-0.0.33.dist-info/METADATA,sha256=9kdhPKSz1FqZiWMkeQq6GFK3TefmevbwWikMIem7BHw,699
|
13
|
+
AIEmailAutomationUtility-0.0.33.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
14
|
+
AIEmailAutomationUtility-0.0.33.dist-info/top_level.txt,sha256=3jTWrTUblVkaP7mpwY2UBSnrlfot5Ykpfsehyke-Uzw,25
|
15
|
+
AIEmailAutomationUtility-0.0.33.dist-info/RECORD,,
|
@@ -1,15 +0,0 @@
|
|
1
|
-
AIEmailAutomationUtility/EmailReplyAssistant.py,sha256=R_wJna3-ITsVxQEccryhM93T_Nf_Oxo8DXnS-sDN8VE,6679
|
2
|
-
AIEmailAutomationUtility/Email_Classification.py,sha256=Ar0g4Ff8HOT7xICktd3nP_C_vCyeY-xCpUjVCVRWAyc,9417
|
3
|
-
AIEmailAutomationUtility/Email_DocumentUploader.py,sha256=YJu4tuTHr0K-5vuds9gZfj-Hwsgm4MuAOP39Lmu_t98,3219
|
4
|
-
AIEmailAutomationUtility/Email_Draft.py,sha256=DcyBeDaE8CReKHnHxLiz-o2tDxuUgwy91c4k0qhQbVw,7749
|
5
|
-
AIEmailAutomationUtility/Email_Read.py,sha256=zE2dK4h0XfKWuSuMJs3E2U-yMPHzRtBfNSdP74h_pDI,30743
|
6
|
-
AIEmailAutomationUtility/Email_Upload_Document.py,sha256=3bdkxfDlwoeRp-46KPw2Gs1dqBhEIoA1yE5GCudpdV8,1320
|
7
|
-
AIEmailAutomationUtility/Process_Category.py,sha256=xXiXYf_dmscKO6scFOMRCWLgKhsV8RsCVK1AXPnE1DI,44695
|
8
|
-
AIEmailAutomationUtility/Save_Draft.py,sha256=yzLgFN14I_lXE6qL0I3tKNduvcnWdbsY9i2mKdTtio4,5348
|
9
|
-
AIEmailAutomationUtility/Save_Transaction.py,sha256=Gg1w6hhzHmEFjsuzYvkq-3-EsWReetjLHsYSv5YIGgM,3816
|
10
|
-
AIEmailAutomationUtility/__init__.py,sha256=Jad3IdPRsVMeLqEEh-FbCrc1lE2tzJO2DTG5Hgmxh5g,391
|
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.32.dist-info → AIEmailAutomationUtility-0.0.33.dist-info}/LICENCE.txt
RENAMED
File without changes
|
{AIEmailAutomationUtility-0.0.32.dist-info → AIEmailAutomationUtility-0.0.33.dist-info}/WHEEL
RENAMED
File without changes
|
File without changes
|