AIEmailAutomationUtility 0.0.33__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.
@@ -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
 
@@ -16,6 +16,8 @@ import csv
16
16
  import os
17
17
  from email import message_from_string
18
18
  import re
19
+ import weaviate
20
+ from weaviate.gql.get import HybridFusion
19
21
 
20
22
  class Process_Category:
21
23
 
@@ -168,8 +170,28 @@ class Process_Category:
168
170
  elif category == "Quotation":
169
171
  action_taken = f"Mail drafted for products rate"
170
172
  responseMethod, parameters = self.get_JsonArray_values(category, file_JsonArray)
171
- logger.log(f"Inside Quotation")
173
+ logger.log(f"Parameters are ::: {parameters}")
172
174
 
175
+
176
+ enterpriseName = parameters["Enterprise_Name"]
177
+ schema_name = parameters["Schema_Name"].capitalize().replace("-","_")
178
+ entity_type = parameters["Entity_Type"]
179
+ server_url = ""
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
+
173
195
  # Step 4: Identify customer from email using AI
174
196
  customer_data = self.identify_customer(email_body, subject, Model_Name, openai_api_key, geminiAI_APIKey, localAIURL, parameters["Customer_Assistant_Id"])
175
197
  logger.log(f"Identified customer is ::: {customer_data}")
@@ -190,6 +212,8 @@ class Process_Category:
190
212
  products = self.identify_products(email_body, subject, Model_Name, openai_api_key, geminiAI_APIKey, localAIURL, parameters["Product_Assistant_Id"])
191
213
 
192
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}')
193
217
 
194
218
  product_determination=products
195
219
 
@@ -198,6 +222,8 @@ class Process_Category:
198
222
  item_no = product.get("item_no", "").strip()
199
223
  make = product.get("make", "").strip()
200
224
  rate = None
225
+ discount = "NA"
226
+ price_pickup_source = "NA"
201
227
  found_rate = False
202
228
 
203
229
  logger.log(f"item no is ::: {item_no}")
@@ -233,8 +259,8 @@ class Process_Category:
233
259
  else:
234
260
  logger.log(f"Process_Category - Quotation [0] No base price found for item '{item_no}' .")
235
261
  product["rate"] = "NA"
236
- product["Price Pickup Source"]="NA"
237
- product["Discount"]="NA"
262
+ product["price_pickup_source"]="NA"
263
+ product["discount"]="NA"
238
264
  continue
239
265
 
240
266
  # Condition 1: Exact match in special_rate_customer_wise
@@ -270,7 +296,7 @@ class Process_Category:
270
296
 
271
297
  if discount_result:
272
298
  discount_percent = discount_result[0]
273
- Discount= discount_percent
299
+ discount= discount_percent
274
300
  rate = raw_price * (1 - int(discount_percent) / 100)
275
301
  rate = round(rate, 2)
276
302
  found_rate = True
@@ -295,7 +321,7 @@ class Process_Category:
295
321
 
296
322
  if past_sales_result:
297
323
  most_common_make, past_discount = past_sales_result
298
- Discount=past_discount
324
+ discount=past_discount
299
325
  if isinstance(raw_price, (int, float)):
300
326
  rate = raw_price * (1 - int(past_discount) / 100)
301
327
  rate = round(rate, 2)
@@ -319,7 +345,7 @@ class Process_Category:
319
345
 
320
346
  if general_discount_result:
321
347
  general_discount_percent = general_discount_result[0]
322
- Discount=general_discount_percent
348
+ discount=general_discount_percent
323
349
  rate = raw_price * (1 - int(general_discount_percent) / 100)
324
350
  rate = round(rate, 2)
325
351
  found_rate = True
@@ -331,11 +357,11 @@ class Process_Category:
331
357
  rate = raw_price
332
358
  logger.log(f"Process_Category - Quotation [5] No discounts applied. Using base price for item '{item_no}': {rate}")
333
359
  price_pickup_source="PRICE_LIST"
334
- Discount = "NA"
360
+ discount = "NA"
335
361
 
336
362
  product["rate"] = rate
337
- product["Price Pickup Source"]=price_pickup_source
338
- product["Discount"]=Discount
363
+ product["price_pickup_source"]=price_pickup_source
364
+ product["discount"]=discount
339
365
 
340
366
  db_connection.close()
341
367
 
@@ -542,7 +568,7 @@ class Process_Category:
542
568
  logger.log("Inside identify_products")
543
569
 
544
570
  if model_type == "OpenAI":
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}. """
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}. """
546
572
  emailreplyassistant = EmailReplyAssistant()
547
573
  ai_result = emailreplyassistant.identify_customer_product_reply_assitant(openai_api_key, assistant_id, email_body, subject, prompt)
548
574
 
@@ -577,7 +603,7 @@ class Process_Category:
577
603
  product_table = "Products:\n"
578
604
  for product in products:
579
605
  rate = product.get("rate", "NA")
580
- quantity = product.get("Quantity", "NA")
606
+ quantity = product.get("quantity", "NA")
581
607
  try:
582
608
  total = float(rate) * float(quantity)
583
609
  except:
@@ -586,14 +612,14 @@ class Process_Category:
586
612
  f'- Requested Description: {product.get("requested_description", "-")}, '
587
613
  f'Item Code: {product.get("item_no", "-")}, '
588
614
  f'Item Description: {product.get("description", "-")}, '
589
- f'Manufacturer: {product.get("make", "-")}, '
615
+ f'Make: {product.get("make", "-")}, '
616
+ f'Inventory Unit: {product.get("inventory_unit", "-")}, '
590
617
  f'Price: {product.get("price", "-")}, '
591
- f'Inventory Unit: {product.get("inventory unit", "-")}, '
592
- f'Discount: {product.get("Discount", "-")}, '
618
+ f'Discount: {product.get("discount", "-")}, '
593
619
  f'Rate: {rate}, '
594
620
  f'Quantity: {quantity}, '
595
621
  f'Total: {total}, '
596
- f'Price Pickup Source: {product.get("Price Pickup Source", "-")}, '
622
+ f'Price Pickup Source: {product.get("price_pickup_source", "-")}, '
597
623
  f'Availability: ""\n'
598
624
  )
599
625
 
@@ -606,11 +632,12 @@ class Process_Category:
606
632
 
607
633
  {product_table}
608
634
  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
635
+ Sr. No., Requested Description, Item Code, Item Description, Make, Inventory Unit, Price, Discount, Rate, Quantity, Total, Price Pickup Source, Availability
610
636
 
611
637
  - If any value is missing, use a dash ("-") instead.
612
638
  - "Total" is calculated as Rate × Quantity.
613
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>).)
614
641
  Original Email Subject: {subject}
615
642
 
616
643
  Return only the following JSON String format:
@@ -976,3 +1003,71 @@ class Process_Category:
976
1003
  return payload.decode(part.get_content_charset() or 'utf-8')
977
1004
  return "No HTML content found."
978
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.33
4
- Summary: Added new columns and Modified the draft prompt to return draft in consistent format for all mails
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
- Added new columns and Modified the draft prompt to return draft in consistent format for all mails
16
+ Added a vector lookup for fallback products those return as NA.
@@ -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=BWNRt2X-E2HCogBaKDfl7cZZNSkZUeIsVs8iXjFjH88,3218
4
4
  AIEmailAutomationUtility/Email_Draft.py,sha256=JYZijUh_zan2asyMYQwIBwIpGNJ5SSQGma5AL1meaXk,7808
5
- AIEmailAutomationUtility/Email_Read.py,sha256=Ehzx9SKjzCgWY9K4rWCnwokDzo6M0Nbhi2XLKYpeEis,31451
5
+ AIEmailAutomationUtility/Email_Read.py,sha256=_JwHTJZmxjuMPTbn90D9YStW7NSJd3t4R_cwUZ1tn20,31470
6
6
  AIEmailAutomationUtility/Email_Upload_Document.py,sha256=3bdkxfDlwoeRp-46KPw2Gs1dqBhEIoA1yE5GCudpdV8,1320
7
- AIEmailAutomationUtility/Process_Category.py,sha256=9X3b2BEw31T6RmJ66g0zMP8WYVlVqi8CDGMIsR6ATbU,47741
7
+ AIEmailAutomationUtility/Process_Category.py,sha256=IMqvDVvuMNeLtQ2heFIyp1UKkz2AN1hAT02lXo8Pywk,53664
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.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,,
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