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.
@@ -6,7 +6,6 @@ import loggerutility as logger
6
6
  from flask import Flask,request
7
7
  from datetime import datetime
8
8
 
9
-
10
9
  class Email_DocumentUploader:
11
10
  def upload_document(self, upload_config, file_data):
12
11
  document_type = upload_config['document_type']
@@ -73,6 +73,7 @@ class Email_Draft:
73
73
  "<html><body>"
74
74
  f"<p>{body_html}</p>"
75
75
  f"{table_html}"
76
+ f"<b>GST Extra as per applicable.</b>"
76
77
  f"<p>{signature_html}</p>"
77
78
  "<hr>"
78
79
  f"<p>{mail_details}</p>"
@@ -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
- if os.path.exists(json_fileName):
560
- with open(json_fileName, "r") as fileObj:
561
- file_JsonArray = json.load(fileObj)
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" and value:
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"{json_fileName} file not found."
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
- new_fileName = self.create_file_from_emailBody(email_body, sender_email_addr, parameters)
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
- if extracted_content != "NA":
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
- product_table += f'- {product.get("requested_description")} (Code: {product.get("item_no")}) = ${product.get("rate")}\n'
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
- product_table must contain price column, if it is none(set it as -).
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
 
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: AIEmailAutomationUtility
3
- Version: 0.0.32
4
- Summary: Make changes in code, should run for windows and ubuntu
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
- Make changes in code, should run for windows and ubuntu
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,,