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.
- AIEmailAutomationUtility/Email_Read.py +1 -1
- AIEmailAutomationUtility/Process_Category.py +111 -16
- {AIEmailAutomationUtility-0.0.33.dist-info → AIEmailAutomationUtility-0.0.34.dist-info}/METADATA +3 -3
- {AIEmailAutomationUtility-0.0.33.dist-info → AIEmailAutomationUtility-0.0.34.dist-info}/RECORD +7 -7
- {AIEmailAutomationUtility-0.0.33.dist-info → AIEmailAutomationUtility-0.0.34.dist-info}/WHEEL +1 -1
- {AIEmailAutomationUtility-0.0.33.dist-info → AIEmailAutomationUtility-0.0.34.dist-info}/LICENCE.txt +0 -0
- {AIEmailAutomationUtility-0.0.33.dist-info → AIEmailAutomationUtility-0.0.34.dist-info}/top_level.txt +0 -0
@@ -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"
|
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["
|
237
|
-
product["
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
360
|
+
discount = "NA"
|
335
361
|
|
336
362
|
product["rate"] = rate
|
337
|
-
product["
|
338
|
-
product["
|
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,
|
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("
|
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'
|
615
|
+
f'Make: {product.get("make", "-")}, '
|
616
|
+
f'Inventory Unit: {product.get("inventory_unit", "-")}, '
|
590
617
|
f'Price: {product.get("price", "-")}, '
|
591
|
-
f'
|
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("
|
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,
|
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)
|
{AIEmailAutomationUtility-0.0.33.dist-info → AIEmailAutomationUtility-0.0.34.dist-info}/METADATA
RENAMED
@@ -1,7 +1,7 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: AIEmailAutomationUtility
|
3
|
-
Version: 0.0.
|
4
|
-
Summary: Added
|
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
|
16
|
+
Added a vector lookup for fallback products those return as NA.
|
{AIEmailAutomationUtility-0.0.33.dist-info → AIEmailAutomationUtility-0.0.34.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=BWNRt2X-E2HCogBaKDfl7cZZNSkZUeIsVs8iXjFjH88,3218
|
4
4
|
AIEmailAutomationUtility/Email_Draft.py,sha256=JYZijUh_zan2asyMYQwIBwIpGNJ5SSQGma5AL1meaXk,7808
|
5
|
-
AIEmailAutomationUtility/Email_Read.py,sha256=
|
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=
|
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.
|
12
|
-
AIEmailAutomationUtility-0.0.
|
13
|
-
AIEmailAutomationUtility-0.0.
|
14
|
-
AIEmailAutomationUtility-0.0.
|
15
|
-
AIEmailAutomationUtility-0.0.
|
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,,
|
{AIEmailAutomationUtility-0.0.33.dist-info → AIEmailAutomationUtility-0.0.34.dist-info}/LICENCE.txt
RENAMED
File without changes
|
File without changes
|