AIEmailAutomationUtility 0.0.32__py3-none-any.whl → 0.0.34__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}")
@@ -277,7 +277,7 @@ class Email_Read:
277
277
  attachments = []
278
278
  status, data = mail.fetch(email_id, '(RFC822 UID)')
279
279
 
280
- if status == 'OK':
280
+ if status == 'OK' and data[0]!= None:
281
281
  raw_email = data[0][1]
282
282
  msg = email.message_from_bytes(raw_email)
283
283
 
@@ -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,10 @@ import openai
14
14
  import json
15
15
  import csv
16
16
  import os
17
+ from email import message_from_string
18
+ import re
19
+ import weaviate
20
+ from weaviate.gql.get import HybridFusion
17
21
 
18
22
  class Process_Category:
19
23
 
@@ -129,7 +133,22 @@ class Process_Category:
129
133
 
130
134
  parameters["DOCUMENT_TYPE"] = document_type
131
135
  logger.log(f"Updated Parameters ::: {parameters}")
132
- new_fileName = self.create_file_from_emailBody(email_body, sender_email_addr, parameters)
136
+
137
+ email_parts = []
138
+ if sender_email_addr:
139
+ email_parts.append(f"From: {sender_email_addr}")
140
+ if to_email_addr:
141
+ email_parts.append(f"To: {to_email_addr}")
142
+ if cc_email_addr:
143
+ email_parts.append(f"CC: {cc_email_addr}")
144
+ if subject:
145
+ email_parts.append(f"Subject: {subject}")
146
+
147
+ email_parts.append(email_body)
148
+ email_body_with_details = "\n".join(email_parts)
149
+
150
+ logger.log(f"email_body ::: {email_body}")
151
+ new_fileName = self.create_file_from_emailBody(email_body_with_details, sender_email_addr, parameters)
133
152
  new_file_path = os.path.join(order_folder, new_fileName)
134
153
 
135
154
  with open(new_file_path, "rb") as file:
@@ -151,8 +170,28 @@ class Process_Category:
151
170
  elif category == "Quotation":
152
171
  action_taken = f"Mail drafted for products rate"
153
172
  responseMethod, parameters = self.get_JsonArray_values(category, file_JsonArray)
154
- logger.log(f"Inside Quotation")
173
+ logger.log(f"Parameters are ::: {parameters}")
174
+
175
+
176
+ enterpriseName = parameters["Enterprise_Name"]
177
+ schema_name = parameters["Schema_Name"].capitalize().replace("-","_")
178
+ entity_type = parameters["Entity_Type"]
179
+ server_url = ""
155
180
 
181
+ schemaName_Updated = enterpriseName + "_" + schema_name + "_" + entity_type
182
+ logger.log(f'\nschemaName_Updated ::: \t{schemaName_Updated}')
183
+
184
+ environment_weaviate_server_url = os.getenv('weaviate_server_url')
185
+ logger.log(f"environment_weaviate_server_url ::: [{environment_weaviate_server_url}]")
186
+
187
+ if environment_weaviate_server_url != None and environment_weaviate_server_url != '':
188
+ server_url = environment_weaviate_server_url
189
+ logger.log(f"\nProcess_cat class Quotation server_url:::\t{server_url} \t{type(server_url)}","0")
190
+ else:
191
+ if 'server_url' in parameters.keys():
192
+ server_url = parameters['server_url']
193
+ logger.log(f"\nProcess_cat class Quotation server_url:::\t{server_url} \t{type(server_url)}","0")
194
+
156
195
  # Step 4: Identify customer from email using AI
157
196
  customer_data = self.identify_customer(email_body, subject, Model_Name, openai_api_key, geminiAI_APIKey, localAIURL, parameters["Customer_Assistant_Id"])
158
197
  logger.log(f"Identified customer is ::: {customer_data}")
@@ -162,18 +201,19 @@ class Process_Category:
162
201
  # Extract customer code once
163
202
  customer_code = customer_data.get("customer_code", "")
164
203
 
165
-
166
- # Step 5: Identify product from email using AI
167
-
204
+ # Step 5: Identify product from email using AI
168
205
  #If there is attachment then append the content in the body.
169
206
  extracted_content = dataValues.get('extracted_content')
170
- if extracted_content != "NA":
207
+ logger.log(f"extracted_content is ::: {extracted_content}")
208
+ if extracted_content != "NA" and extracted_content != None:
171
209
  attachment_email_body =email_body + " \n\n" + extracted_content
172
210
  products = self.identify_products(attachment_email_body, subject, Model_Name, openai_api_key, geminiAI_APIKey, localAIURL, parameters["Product_Assistant_Id"])
173
211
  else:
174
212
  products = self.identify_products(email_body, subject, Model_Name, openai_api_key, geminiAI_APIKey, localAIURL, parameters["Product_Assistant_Id"])
175
213
 
176
214
  logger.log(f'Identified Products are ::: {products}')
215
+ products=self.products_item_code_lookup(products, openai_api_key, schemaName_Updated, server_url)
216
+ logger.log(f'Identified Products after Lookup are ::: {products}')
177
217
 
178
218
  product_determination=products
179
219
 
@@ -182,6 +222,8 @@ class Process_Category:
182
222
  item_no = product.get("item_no", "").strip()
183
223
  make = product.get("make", "").strip()
184
224
  rate = None
225
+ discount = "NA"
226
+ price_pickup_source = "NA"
185
227
  found_rate = False
186
228
 
187
229
  logger.log(f"item no is ::: {item_no}")
@@ -217,8 +259,8 @@ class Process_Category:
217
259
  else:
218
260
  logger.log(f"Process_Category - Quotation [0] No base price found for item '{item_no}' .")
219
261
  product["rate"] = "NA"
220
- product["Price Pickup Source"]="NA"
221
- product["Discount"]="NA"
262
+ product["price_pickup_source"]="NA"
263
+ product["discount"]="NA"
222
264
  continue
223
265
 
224
266
  # Condition 1: Exact match in special_rate_customer_wise
@@ -254,7 +296,7 @@ class Process_Category:
254
296
 
255
297
  if discount_result:
256
298
  discount_percent = discount_result[0]
257
- Discount= discount_percent
299
+ discount= discount_percent
258
300
  rate = raw_price * (1 - int(discount_percent) / 100)
259
301
  rate = round(rate, 2)
260
302
  found_rate = True
@@ -279,7 +321,7 @@ class Process_Category:
279
321
 
280
322
  if past_sales_result:
281
323
  most_common_make, past_discount = past_sales_result
282
- Discount=past_discount
324
+ discount=past_discount
283
325
  if isinstance(raw_price, (int, float)):
284
326
  rate = raw_price * (1 - int(past_discount) / 100)
285
327
  rate = round(rate, 2)
@@ -303,7 +345,7 @@ class Process_Category:
303
345
 
304
346
  if general_discount_result:
305
347
  general_discount_percent = general_discount_result[0]
306
- Discount=general_discount_percent
348
+ discount=general_discount_percent
307
349
  rate = raw_price * (1 - int(general_discount_percent) / 100)
308
350
  rate = round(rate, 2)
309
351
  found_rate = True
@@ -315,11 +357,11 @@ class Process_Category:
315
357
  rate = raw_price
316
358
  logger.log(f"Process_Category - Quotation [5] No discounts applied. Using base price for item '{item_no}': {rate}")
317
359
  price_pickup_source="PRICE_LIST"
318
- Discount = "NA"
360
+ discount = "NA"
319
361
 
320
362
  product["rate"] = rate
321
- product["Price Pickup Source"]=price_pickup_source
322
- product["Discount"]=Discount
363
+ product["price_pickup_source"]=price_pickup_source
364
+ product["discount"]=discount
323
365
 
324
366
  db_connection.close()
325
367
 
@@ -526,7 +568,7 @@ class Process_Category:
526
568
  logger.log("Inside identify_products")
527
569
 
528
570
  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}. """
571
+ 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 array format(Always return the result as a JSON **array**, even if there's only one item). 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
572
  emailreplyassistant = EmailReplyAssistant()
531
573
  ai_result = emailreplyassistant.identify_customer_product_reply_assitant(openai_api_key, assistant_id, email_body, subject, prompt)
532
574
 
@@ -555,13 +597,31 @@ class Process_Category:
555
597
  return product_data
556
598
 
557
599
  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
600
 
560
601
  customer = customer_data
561
602
 
562
603
  product_table = "Products:\n"
563
604
  for product in products:
564
- product_table += f'- {product.get("requested_description")} (Code: {product.get("item_no")}) = ${product.get("rate")}\n'
605
+ rate = product.get("rate", "NA")
606
+ quantity = product.get("quantity", "NA")
607
+ try:
608
+ total = float(rate) * float(quantity)
609
+ except:
610
+ total = rate if rate != "NA" and rate not in ("", None) else "-"
611
+ product_table += (
612
+ f'- Requested Description: {product.get("requested_description", "-")}, '
613
+ f'Item Code: {product.get("item_no", "-")}, '
614
+ f'Item Description: {product.get("description", "-")}, '
615
+ f'Make: {product.get("make", "-")}, '
616
+ f'Inventory Unit: {product.get("inventory_unit", "-")}, '
617
+ f'Price: {product.get("price", "-")}, '
618
+ f'Discount: {product.get("discount", "-")}, '
619
+ f'Rate: {rate}, '
620
+ f'Quantity: {quantity}, '
621
+ f'Total: {total}, '
622
+ f'Price Pickup Source: {product.get("price_pickup_source", "-")}, '
623
+ f'Availability: ""\n'
624
+ )
565
625
 
566
626
  if model_type == "OpenAI":
567
627
  prompt = f"""
@@ -571,7 +631,13 @@ class Process_Category:
571
631
  Customer Code: {customer.get('customer_code', '')}
572
632
 
573
633
  {product_table}
574
- product_table must contain price column, if it is none(set it as -).
634
+ Ensure the table has the following columns in this exact order:
635
+ Sr. No., Requested Description, Item Code, Item Description, Make, Inventory Unit, Price, Discount, Rate, Quantity, Total, Price Pickup Source, Availability
636
+
637
+ - If any value is missing, use a dash ("-") instead.
638
+ - "Total" is calculated as Rate × Quantity.
639
+ - "Availability" should be a blank column.
640
+ - Only the numeric values in the table rows are right-aligned(Ensure the alignment applies only to the data cells (i.e., <td>), not the header cells (<th>).)
575
641
  Original Email Subject: {subject}
576
642
 
577
643
  Return only the following JSON String format:
@@ -788,6 +854,17 @@ class Process_Category:
788
854
  with the name user_id.csv.
789
855
  """
790
856
 
857
+ logger.log(f"The response am getting in csv is : {response}")
858
+ match = re.search(
859
+ r'Content-Transfer-Encoding:\s*base64\s+([\s\S]+?)\n--',
860
+ response,
861
+ re.IGNORECASE
862
+ )
863
+ if match:
864
+ response = self.extract_html_from_mime(response)
865
+
866
+ logger.log(f"The response after am getting in csv is : {response}")
867
+
791
868
  # Ensure the Mail_log folder exists
792
869
  log_folder = "Mail_log"
793
870
  os.makedirs(log_folder, exist_ok=True)
@@ -911,4 +988,86 @@ class Process_Category:
911
988
  if user_id:
912
989
  return user_id.value
913
990
  return None
991
+
992
+ def extract_html_from_mime(self, raw_data):
993
+ msg = message_from_string(raw_data)
994
+
995
+ if msg.is_multipart():
996
+ for part in msg.walk():
997
+ content_type = part.get_content_type()
998
+ content_encoding = part.get("Content-Transfer-Encoding", "").lower()
999
+
1000
+ # Look for text/html part with base64 encoding
1001
+ if content_type == "text/html" and content_encoding == "base64":
1002
+ payload = part.get_payload(decode=True)
1003
+ return payload.decode(part.get_content_charset() or 'utf-8')
1004
+ return "No HTML content found."
914
1005
 
1006
+ def products_item_code_lookup(self, products, openai_api_key, schemaName_Updated, server_url):
1007
+ try:
1008
+ logger.log(f'\nproduct_Json : {products}')
1009
+ logger.log(f'\nopenai_api_key : {openai_api_key}')
1010
+ logger.log(f'\nschemaName_Updated : {schemaName_Updated}')
1011
+ logger.log(f'\nserver_url : {server_url}')
1012
+ alphaValue = 0.54
1013
+
1014
+ client = weaviate.Client(server_url,additional_headers={"X-OpenAI-Api-Key": openai_api_key})
1015
+ logger.log(f'Connection is establish : {client.is_ready()}')
1016
+
1017
+ schemaClasslist = [i['class'] for i in client.schema.get()["classes"]]
1018
+ logger.log(f'schemaClasslist : {schemaClasslist}')
1019
+
1020
+ for product in products:
1021
+ item_no = product.get("item_no", "NA")
1022
+ item_name = product.get("requested_description", "NA")
1023
+ if item_no == "NA":
1024
+ inputQuery = item_name.upper().replace("N/A","").replace("."," ").replace(","," ").replace("-"," ").replace("_"," ")
1025
+ logger.log(f'inputQuery : {inputQuery}')
1026
+
1027
+ if schemaName_Updated in schemaClasslist:
1028
+ logger.log(f'Inside schemaClasslist')
1029
+ response = (
1030
+ client.query
1031
+ .get(schemaName_Updated, ["description", "answer","phy_attrib_2","phy_attrib_3","phy_attrib_4"])
1032
+ .with_hybrid(
1033
+ alpha = alphaValue,
1034
+ query = inputQuery.strip() ,
1035
+ fusion_type = HybridFusion.RELATIVE_SCORE
1036
+ )
1037
+ .with_additional('score')
1038
+ .with_limit(10)
1039
+ .do()
1040
+ )
1041
+ logger.log(f"Input ::: {item_name}")
1042
+ if response != {}:
1043
+ response_List = response['data']['Get'][schemaName_Updated]
1044
+ product['description'] = response_List[0]['description']
1045
+ product['item_no'] = response_List[0]['answer']
1046
+ # product['cas_no'] = response_List[0]['phy_attrib_1']
1047
+ product['make'] = response_List[0]['phy_attrib_2']
1048
+ product["price"] = response_List[0]['phy_attrib_3']
1049
+ product["inventory_unit"] = response_List[0]['phy_attrib_4']
1050
+
1051
+ for index in range(len(response_List)):
1052
+ description = response_List[index]['description']
1053
+ description = description.upper().replace("N/A","").replace("."," ").replace(","," ").replace("-"," ").replace("_"," ")
1054
+
1055
+ descr_replaced = description.replace(" ", "")
1056
+ inputQuery_replaced = inputQuery.replace(" ", "")
1057
+
1058
+ if descr_replaced == inputQuery_replaced:
1059
+ logger.log(f"\n Input::: '{inputQuery_replaced}' MATCHEDD with description ::: '{descr_replaced}' \n")
1060
+ product['description'] = response_List[index]['description']
1061
+ product['item_no'] = response_List[index]['answer']
1062
+ # product['cas_no'] = response_List[index]['phy_attrib_1']
1063
+ product['make'] = response_List[index]['phy_attrib_2']
1064
+ product["price"] = response_List[index]['phy_attrib_3']
1065
+ product["inventory_unit"] = response_List[index]['phy_attrib_4']
1066
+ break
1067
+ else:
1068
+ logger.log(f"\n Input '{inputQuery_replaced}' not matched with returned response description '{descr_replaced}'\n ")
1069
+
1070
+ return products
1071
+
1072
+ except Exception as error:
1073
+ raise str(error)
@@ -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.34
4
+ Summary: Added a vector lookup for fallback products those return as NA.
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 a vector lookup for fallback products those return as NA.
@@ -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=_JwHTJZmxjuMPTbn90D9YStW7NSJd3t4R_cwUZ1tn20,31470
6
+ AIEmailAutomationUtility/Email_Upload_Document.py,sha256=3bdkxfDlwoeRp-46KPw2Gs1dqBhEIoA1yE5GCudpdV8,1320
7
+ AIEmailAutomationUtility/Process_Category.py,sha256=IMqvDVvuMNeLtQ2heFIyp1UKkz2AN1hAT02lXo8Pywk,53664
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.34.dist-info/LICENCE.txt,sha256=2qX9IkEUBx0VJp1Vh9O2dsRwE-IpYId0lXDyn7OVsJ8,1073
12
+ AIEmailAutomationUtility-0.0.34.dist-info/METADATA,sha256=eCpEKv1Cvin8-cceIQZ6OvbOsZ36o9EeijDAYgrM7Pw,627
13
+ AIEmailAutomationUtility-0.0.34.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
14
+ AIEmailAutomationUtility-0.0.34.dist-info/top_level.txt,sha256=3jTWrTUblVkaP7mpwY2UBSnrlfot5Ykpfsehyke-Uzw,25
15
+ AIEmailAutomationUtility-0.0.34.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.43.0)
2
+ Generator: bdist_wheel (0.45.1)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -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,,