AIEmailAutomationUtility 0.0.26__py3-none-any.whl → 0.0.27__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/EmailReplyAssistant.py +9 -2
- AIEmailAutomationUtility/Email_Read.py +2 -1
- AIEmailAutomationUtility/Process_Category.py +88 -47
- {AIEmailAutomationUtility-0.0.26.dist-info → AIEmailAutomationUtility-0.0.27.dist-info}/METADATA +3 -3
- {AIEmailAutomationUtility-0.0.26.dist-info → AIEmailAutomationUtility-0.0.27.dist-info}/RECORD +8 -8
- {AIEmailAutomationUtility-0.0.26.dist-info → AIEmailAutomationUtility-0.0.27.dist-info}/LICENCE.txt +0 -0
- {AIEmailAutomationUtility-0.0.26.dist-info → AIEmailAutomationUtility-0.0.27.dist-info}/WHEEL +0 -0
- {AIEmailAutomationUtility-0.0.26.dist-info → AIEmailAutomationUtility-0.0.27.dist-info}/top_level.txt +0 -0
@@ -95,6 +95,12 @@ class EmailReplyAssistant:
|
|
95
95
|
|
96
96
|
def identify_customer_product_reply_assitant(self, openAI_key, assistant_ID, email_content, subject, prompt):
|
97
97
|
try:
|
98
|
+
logger.log(f"the Assistant id is ::: {assistant_ID}")
|
99
|
+
logger.log(f"the Prompt is ::: {prompt}")
|
100
|
+
logger.log(f"the Api key is ::: {openAI_key}")
|
101
|
+
logger.log(f"the email content is ::: {email_content}")
|
102
|
+
logger.log(f"the subject is ::: {subject}")
|
103
|
+
|
98
104
|
openAI_response = ""
|
99
105
|
client = OpenAI(api_key=openAI_key)
|
100
106
|
thread = client.beta.threads.create()
|
@@ -102,7 +108,8 @@ class EmailReplyAssistant:
|
|
102
108
|
client.beta.threads.messages.create(
|
103
109
|
thread_id=thread.id,
|
104
110
|
role="user",
|
105
|
-
content=f"subject:{subject}\nemail body:{email_content}",
|
111
|
+
# content=f"subject:{subject}\nemail body:{email_content}",
|
112
|
+
content=prompt,
|
106
113
|
)
|
107
114
|
|
108
115
|
event_handler = EmailReplyAssistant().EventHandler()
|
@@ -110,7 +117,7 @@ class EmailReplyAssistant:
|
|
110
117
|
with client.beta.threads.runs.stream(
|
111
118
|
thread_id=thread.id,
|
112
119
|
assistant_id=assistant_ID,
|
113
|
-
instructions=prompt,
|
120
|
+
# instructions=prompt,
|
114
121
|
event_handler=event_handler,
|
115
122
|
) as stream:
|
116
123
|
stream.until_done()
|
@@ -552,7 +552,8 @@ class Email_Read:
|
|
552
552
|
logger.log(f"Exception in writeJsonFile: {msg} \n {trace} \n DataType ::: {type(msg)}")
|
553
553
|
raise Exception(msg)
|
554
554
|
|
555
|
-
def log_email_login(self, user_id, email, model_name, login_status
|
555
|
+
def log_email_login(self, user_id, email, model_name, login_status):
|
556
|
+
base_dir="EMail_log"
|
556
557
|
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
557
558
|
log_dir = os.path.join(base_dir, user_id)
|
558
559
|
os.makedirs(log_dir, exist_ok=True)
|
@@ -42,16 +42,14 @@ class Process_Category:
|
|
42
42
|
is_html = dataValues.get('is_html')
|
43
43
|
import_file = dataValues.get('import_file')
|
44
44
|
|
45
|
-
|
46
|
-
|
47
|
-
logger.log(f"the json datavalues of email_id are ------------------------ {email_id}")
|
48
|
-
logger.log(f"the category is ------------------------ {category}")
|
45
|
+
customer_determination = None
|
46
|
+
product_determination = None
|
49
47
|
|
50
48
|
if category == "Product Enquiry":
|
51
49
|
if Model_Name == "OpenAI":
|
52
50
|
action_taken = "Reply email drafted using Open_AI Model"
|
53
51
|
responseMethod, parameters = self.get_JsonArray_values(category, file_JsonArray)
|
54
|
-
logger.log(f"the repsonse method{responseMethod}")
|
52
|
+
logger.log(f"the repsonse method is ::: {responseMethod}")
|
55
53
|
if responseMethod == "Reply_Email_Ai_Assistant":
|
56
54
|
emailreplyassistant = EmailReplyAssistant()
|
57
55
|
openai_Response = emailreplyassistant.Reply_Email_Ai_Assistant(openai_api_key, parameters["Assistant_Id"], openai_Process_Input, subject)
|
@@ -156,32 +154,39 @@ class Process_Category:
|
|
156
154
|
|
157
155
|
# Step 4: Identify customer from email using AI
|
158
156
|
customer_data = self.identify_customer(email_body, subject, Model_Name, openai_api_key, geminiAI_APIKey, localAIURL, parameters["Customer_Assistant_Id"])
|
159
|
-
logger.log(f"Identified customer
|
160
|
-
|
157
|
+
logger.log(f"Identified customer is ::: {customer_data}")
|
158
|
+
|
159
|
+
customer_determination=customer_data
|
160
|
+
|
161
161
|
# Extract customer code once
|
162
162
|
customer_code = customer_data.get("customer_code", "")
|
163
163
|
|
164
164
|
# Step 5: Identify product from email using AI
|
165
165
|
products = self.identify_products(email_body, subject, Model_Name, openai_api_key, geminiAI_APIKey, localAIURL, parameters["Product_Assistant_Id"])
|
166
|
-
logger.log(f'
|
166
|
+
logger.log(f'Identified Products are ::: {products}')
|
167
|
+
|
168
|
+
product_determination=products
|
167
169
|
|
168
170
|
db_connection = sqlite3.connect('/home/base/git/ai-email-automation/AI_Email/database/fetchprice.db')
|
169
171
|
for product in products:
|
170
|
-
cursor = db_connection.cursor()
|
171
|
-
|
172
172
|
item_no = product.get("item_no", "").strip()
|
173
173
|
make = product.get("make", "").strip()
|
174
174
|
rate = None
|
175
175
|
found_rate = False
|
176
176
|
|
177
|
+
logger.log(f"item no is ::: {item_no}")
|
178
|
+
logger.log(f"make is ::: {make}")
|
179
|
+
|
177
180
|
# Step 1: Get base price from PRICE_LIST
|
178
181
|
query_price = '''
|
179
182
|
SELECT PRICE
|
180
183
|
FROM PRICE_LIST
|
181
184
|
WHERE ITEM_NO = ?;
|
182
185
|
'''
|
186
|
+
cursor = db_connection.cursor()
|
183
187
|
cursor.execute(query_price, (item_no,))
|
184
188
|
price_result = cursor.fetchone()
|
189
|
+
cursor.close()
|
185
190
|
|
186
191
|
if price_result:
|
187
192
|
price_raw = price_result[0]
|
@@ -194,17 +199,16 @@ class Process_Category:
|
|
194
199
|
|
195
200
|
try:
|
196
201
|
raw_price = float(price_cleaned)
|
197
|
-
logger.log(f"[0] Base price for item '{item_no}' is {raw_price}")
|
202
|
+
logger.log(f"Process_Category - Quotation [0] Base price for item '{item_no}' is {raw_price}")
|
198
203
|
except (TypeError, ValueError):
|
199
|
-
logger.log(f"[0] Invalid raw price for item '{item_no}': {price_result[0]}")
|
204
|
+
logger.log(f"Process_Category - Quotation [0] Invalid raw price for item '{item_no}': {price_result[0]}")
|
200
205
|
product["rate"] = None
|
201
206
|
continue
|
202
207
|
else:
|
203
|
-
logger.log(f"[0] No base price found for item '{item_no}'.
|
204
|
-
product["rate"] =
|
208
|
+
logger.log(f"Process_Category - Quotation [0] No base price found for item '{item_no}' .")
|
209
|
+
product["rate"] = "NA"
|
205
210
|
continue
|
206
211
|
|
207
|
-
|
208
212
|
# Condition 1: Exact match in special_rate_customer_wise
|
209
213
|
query1 = '''
|
210
214
|
SELECT RATE
|
@@ -212,13 +216,16 @@ class Process_Category:
|
|
212
216
|
WHERE ITEM_CODE = ?
|
213
217
|
AND CUSTOMER_CODE = ?;
|
214
218
|
'''
|
219
|
+
cursor = db_connection.cursor()
|
215
220
|
cursor.execute(query1, (item_no, customer_code))
|
216
221
|
result = cursor.fetchone()
|
222
|
+
cursor.close()
|
217
223
|
|
218
224
|
if result:
|
219
225
|
rate = result[0]
|
220
226
|
found_rate = True
|
221
|
-
logger.log(f"[1] Special Rate for item '{item_no}' and customer '{customer_code}' is {rate}")
|
227
|
+
logger.log(f"Process_Category - Quotation [1] Special Rate for item '{item_no}' and customer '{customer_code}' is {rate}")
|
228
|
+
price_pickup_source="SPECIAL_RATE_CUSTOMER_WISE"
|
222
229
|
|
223
230
|
# Condition 2: Customer + Manufacturer discount
|
224
231
|
if not found_rate:
|
@@ -227,15 +234,18 @@ class Process_Category:
|
|
227
234
|
FROM CUSTOMER_WISE_DISCOUNT
|
228
235
|
WHERE CUSTOMER_CODE = ? AND MAKE = ?;
|
229
236
|
'''
|
237
|
+
cursor = db_connection.cursor()
|
230
238
|
cursor.execute(query2, (customer_code, make))
|
231
239
|
discount_result = cursor.fetchone()
|
240
|
+
cursor.close()
|
232
241
|
|
233
242
|
if discount_result:
|
234
243
|
discount_percent = discount_result[0]
|
235
244
|
rate = raw_price * (1 - int(discount_percent) / 100)
|
236
245
|
rate = round(rate, 2)
|
237
246
|
found_rate = True
|
238
|
-
logger.log(f"[2] Discounted rate for '{make}' ({discount_percent}%) on price {raw_price}: {rate}")
|
247
|
+
logger.log(f"Process_Category - Quotation [2] Discounted rate for '{make}' ({discount_percent}%) on price {raw_price}: {rate}")
|
248
|
+
price_pickup_source="CUSTOMER_WISE_DISCOUNT"
|
239
249
|
|
240
250
|
# Condition 3: Past Sales most used make
|
241
251
|
if not found_rate:
|
@@ -248,8 +258,10 @@ class Process_Category:
|
|
248
258
|
ORDER BY COUNT(*) DESC
|
249
259
|
LIMIT 1;
|
250
260
|
'''
|
261
|
+
cursor = db_connection.cursor()
|
251
262
|
cursor.execute(query3, (customer_code, item_no))
|
252
263
|
past_sales_result = cursor.fetchone()
|
264
|
+
cursor.close()
|
253
265
|
|
254
266
|
if past_sales_result:
|
255
267
|
most_common_make, past_discount = past_sales_result
|
@@ -257,12 +269,11 @@ class Process_Category:
|
|
257
269
|
rate = raw_price * (1 - int(past_discount) / 100)
|
258
270
|
rate = round(rate, 2)
|
259
271
|
found_rate = True
|
260
|
-
logger.log(f"[3] Fallback: Most used make '{most_common_make}' for customer '{customer_code}' got {past_discount}% discount. Rate: {rate}")
|
272
|
+
logger.log(f"Process_Category - Quotation [3] Fallback: Most used make '{most_common_make}' for customer '{customer_code}' got {past_discount}% discount. Rate: {rate}")
|
273
|
+
price_pickup_source="PAST_SALES"
|
261
274
|
else:
|
262
|
-
logger.log(f"[3] Fallback: Invalid price for item '{item_no}'")
|
263
|
-
|
264
|
-
logger.log(f"[3] No past sales data for item '{item_no}' and customer '{customer_code}'")
|
265
|
-
|
275
|
+
logger.log(f"Process_Category - Quotation [3] Fallback: Invalid price for item '{item_no}'")
|
276
|
+
|
266
277
|
# Condition 4: Manufacturer General Discount
|
267
278
|
if not found_rate:
|
268
279
|
query4 = '''
|
@@ -270,20 +281,27 @@ class Process_Category:
|
|
270
281
|
FROM MANUFACTURE_WISE_GENERAL_DISCOUNT
|
271
282
|
WHERE MAKE = ?;
|
272
283
|
'''
|
284
|
+
cursor = db_connection.cursor()
|
273
285
|
cursor.execute(query4, (make,))
|
274
286
|
general_discount_result = cursor.fetchone()
|
287
|
+
cursor.close()
|
275
288
|
|
276
289
|
if general_discount_result:
|
277
290
|
general_discount_percent = general_discount_result[0]
|
278
291
|
rate = raw_price * (1 - int(general_discount_percent) / 100)
|
279
292
|
rate = round(rate, 2)
|
280
293
|
found_rate = True
|
281
|
-
logger.log(f"[4] General Discount for '{make}' ({general_discount_percent}%) on price {raw_price}: {rate}")
|
282
|
-
|
283
|
-
|
294
|
+
logger.log(f"Process_Category - Quotation [4] General Discount for '{make}' ({general_discount_percent}%) on price {raw_price}: {rate}")
|
295
|
+
price_pickup_source="MANUFACTURE_WISE_GENERAL_DISCOUNT"
|
296
|
+
|
297
|
+
#Condition 5: Fallback to raw_price if no discount applied
|
298
|
+
if not found_rate:
|
299
|
+
rate = raw_price
|
300
|
+
logger.log(f"Process_Category - Quotation [5] No discounts applied. Using base price for item '{item_no}': {rate}")
|
301
|
+
price_pickup_source="PRICE_LIST"
|
284
302
|
|
285
303
|
product["rate"] = rate
|
286
|
-
|
304
|
+
product["Price Pickup Source"]=price_pickup_source
|
287
305
|
|
288
306
|
db_connection.close()
|
289
307
|
|
@@ -311,8 +329,6 @@ class Process_Category:
|
|
311
329
|
|
312
330
|
csv_data_status = status
|
313
331
|
csv_data_response = response
|
314
|
-
# csv_data_status = "status"
|
315
|
-
# csv_data_response = "response"
|
316
332
|
logger.log(f"Quotation email sent to {sender_email_addr}")
|
317
333
|
|
318
334
|
elif category == "Others" and import_file == True:
|
@@ -325,7 +341,7 @@ class Process_Category:
|
|
325
341
|
csv_data_response = f""
|
326
342
|
|
327
343
|
logger.log(f"Marking email as UNREAD. ")
|
328
|
-
|
344
|
+
# print(f"email_id: {email_id}, type: {type(email_id)}")
|
329
345
|
mail.store(email_id, '-FLAGS', '\\Seen')
|
330
346
|
mail.create(LABEL)
|
331
347
|
mail.copy(email_id, LABEL)
|
@@ -335,7 +351,7 @@ class Process_Category:
|
|
335
351
|
|
336
352
|
current_timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
337
353
|
filename = f'{uid}{current_timestamp}'
|
338
|
-
logger.log(f"
|
354
|
+
logger.log(f"The file name for csv and eml is ::: {filename}")
|
339
355
|
self.store_email_details_to_csv(
|
340
356
|
email_id=f'{uid}{current_timestamp}',
|
341
357
|
to_email=parseaddr(to_email_addr)[1],
|
@@ -349,7 +365,9 @@ class Process_Category:
|
|
349
365
|
user_id=user_id,
|
350
366
|
status=csv_data_status,
|
351
367
|
response=csv_data_response,
|
352
|
-
current_timestamp=current_timestamp
|
368
|
+
current_timestamp=current_timestamp,
|
369
|
+
customer_determination=customer_determination,
|
370
|
+
product_determination=product_determination,
|
353
371
|
)
|
354
372
|
self.store_email_as_eml(
|
355
373
|
uid=f'{uid}{current_timestamp}',
|
@@ -460,10 +478,10 @@ class Process_Category:
|
|
460
478
|
logger.log("Inside identify_customer")
|
461
479
|
|
462
480
|
if model_type == "OpenAI":
|
463
|
-
prompt = f"""Identify the customer code, customer name
|
481
|
+
prompt = f"""/* Identify the most likely customer code, customer name from the following email using the file attached in assistant. Use various key information's such as sender name, organisation, address, email id to determine the customer. If customer not available in the data return the name and NA as code. Return the data in json format. Always return 1 row. Do not include any instruction as the output will be directly in a program. */ /n {email_body}"""
|
464
482
|
emailreplyassistant = EmailReplyAssistant()
|
465
483
|
ai_result = emailreplyassistant.identify_customer_product_reply_assitant(openai_api_key, assistant_id, email_body, subject, prompt)
|
466
|
-
|
484
|
+
|
467
485
|
elif model_type == "GeminiAI":
|
468
486
|
prompt = f"""Identify the customer code, customer name in json format from the following email {email_body} and received from {subject}. Do not include any instruction as the output will be directly in a program."""
|
469
487
|
ai_result = self.identify_customer_product_GeminiAI(gemini_api_key, email_body, prompt)
|
@@ -490,11 +508,7 @@ class Process_Category:
|
|
490
508
|
logger.log("Inside identify_products")
|
491
509
|
|
492
510
|
if model_type == "OpenAI":
|
493
|
-
prompt = f"""
|
494
|
-
Can you give me price information of all products in following format requested_description, item_no, make, description, price, price unit, inventory unit for following items in strictly in JSON String format {email_body}.
|
495
|
-
If there is one product or multiple should return in list.
|
496
|
-
Do not include any instruction as the output will be directly in a program.
|
497
|
-
"""
|
511
|
+
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}. """
|
498
512
|
emailreplyassistant = EmailReplyAssistant()
|
499
513
|
ai_result = emailreplyassistant.identify_customer_product_reply_assitant(openai_api_key, assistant_id, email_body, subject, prompt)
|
500
514
|
|
@@ -539,7 +553,7 @@ class Process_Category:
|
|
539
553
|
Customer Code: {customer.get('customer_code', '')}
|
540
554
|
|
541
555
|
{product_table}
|
542
|
-
product_table must contain
|
556
|
+
product_table must contain price column, if it is none(set it as -).
|
543
557
|
Original Email Subject: {subject}
|
544
558
|
|
545
559
|
Return only the following JSON String format:
|
@@ -750,7 +764,7 @@ class Process_Category:
|
|
750
764
|
logger.log(f"Error with Gemini AI detection/generation: {str(e)}")
|
751
765
|
return {"success": "Failed", "message": f"Error with Gemini AI detection/generation: {str(e)}"}
|
752
766
|
|
753
|
-
def store_email_details_to_csv(self, email_id, to_email, from_email, cc_email, subject, body, email_type, action_performed, filename, user_id,status,response,current_timestamp):
|
767
|
+
def store_email_details_to_csv(self, email_id, to_email, from_email, cc_email, subject, body, email_type, action_performed, filename, user_id,status,response,current_timestamp,customer_determination,product_determination):
|
754
768
|
"""
|
755
769
|
Stores the extracted email details to a CSV file inside 'Mail_log/mail_log_user_id' folder
|
756
770
|
with the name user_id.csv.
|
@@ -765,28 +779,55 @@ class Process_Category:
|
|
765
779
|
os.makedirs(user_folder, exist_ok=True)
|
766
780
|
|
767
781
|
filename = filename.lstrip()
|
768
|
-
logger.log(f"filename ::: [{filename}]")
|
769
782
|
full_csv_path = os.path.join(user_folder, f"{filename}.csv")
|
770
|
-
logger.log(f"full_csv_path ::: {full_csv_path}")
|
771
783
|
|
772
|
-
|
773
|
-
|
784
|
+
if email_type=="Quotation":
|
785
|
+
csv_data = [{
|
774
786
|
'to': to_email,
|
775
787
|
'from': from_email,
|
776
788
|
'cc': cc_email,
|
777
789
|
'subject': subject,
|
778
790
|
'body': body.replace('\n', ' ').replace('\r', ''),
|
779
791
|
'Category': email_type,
|
780
|
-
'Action
|
792
|
+
'Action Performed': action_performed,
|
781
793
|
'unique_id': email_id,
|
782
794
|
'timestamp': current_timestamp,
|
783
795
|
'status of mail draft': status,
|
784
|
-
'Response
|
796
|
+
'Response Generated':response,
|
797
|
+
'Customer Determination':customer_determination,
|
798
|
+
'Product Determination':product_determination,
|
785
799
|
}]
|
800
|
+
else:
|
801
|
+
csv_data = [{
|
802
|
+
'to': to_email,
|
803
|
+
'from': from_email,
|
804
|
+
'cc': cc_email,
|
805
|
+
'subject': subject,
|
806
|
+
'body': body.replace('\n', ' ').replace('\r', ''),
|
807
|
+
'Category': email_type,
|
808
|
+
'Action Performed': action_performed,
|
809
|
+
'unique_id': email_id,
|
810
|
+
'timestamp': current_timestamp,
|
811
|
+
'status of mail draft': status,
|
812
|
+
'Response Generated':response
|
813
|
+
}]
|
786
814
|
|
787
815
|
# Write to CSV file (user_id.csv)
|
788
816
|
with open(full_csv_path, 'a', newline='', encoding='utf-8') as csvfile:
|
789
|
-
|
817
|
+
if email_type == "Quotation":
|
818
|
+
fieldnames = [
|
819
|
+
'to', 'from', 'cc', 'timestamp', 'subject', 'body',
|
820
|
+
'Category', 'Action Performed', 'unique_id',
|
821
|
+
'status of mail draft', 'Response Generated',
|
822
|
+
'Customer Determination', 'Product Determination'
|
823
|
+
]
|
824
|
+
else:
|
825
|
+
fieldnames = [
|
826
|
+
'to', 'from', 'cc', 'timestamp', 'subject', 'body',
|
827
|
+
'Category', 'Action Performed', 'unique_id',
|
828
|
+
'status of mail draft', 'Response Generated'
|
829
|
+
]
|
830
|
+
|
790
831
|
writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
|
791
832
|
|
792
833
|
# If the file is empty, write the header
|
{AIEmailAutomationUtility-0.0.26.dist-info → AIEmailAutomationUtility-0.0.27.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.27
|
4
|
+
Summary: Added 3 more parameters in the right panel of quotation email utilty
|
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 3 more parameters in the right panel of quotation email utilty
|
{AIEmailAutomationUtility-0.0.26.dist-info → AIEmailAutomationUtility-0.0.27.dist-info}/RECORD
RENAMED
@@ -1,15 +1,15 @@
|
|
1
|
-
AIEmailAutomationUtility/EmailReplyAssistant.py,sha256=
|
1
|
+
AIEmailAutomationUtility/EmailReplyAssistant.py,sha256=R_wJna3-ITsVxQEccryhM93T_Nf_Oxo8DXnS-sDN8VE,6679
|
2
2
|
AIEmailAutomationUtility/Email_Classification.py,sha256=Ar0g4Ff8HOT7xICktd3nP_C_vCyeY-xCpUjVCVRWAyc,9417
|
3
3
|
AIEmailAutomationUtility/Email_DocumentUploader.py,sha256=YJu4tuTHr0K-5vuds9gZfj-Hwsgm4MuAOP39Lmu_t98,3219
|
4
4
|
AIEmailAutomationUtility/Email_Draft.py,sha256=DcyBeDaE8CReKHnHxLiz-o2tDxuUgwy91c4k0qhQbVw,7749
|
5
|
-
AIEmailAutomationUtility/Email_Read.py,sha256=
|
5
|
+
AIEmailAutomationUtility/Email_Read.py,sha256=pRqc9295r_qqdfWIFJFG2XH9J-VlvO1E0yP3iv_cRV4,27802
|
6
6
|
AIEmailAutomationUtility/Email_Upload_Document.py,sha256=3bdkxfDlwoeRp-46KPw2Gs1dqBhEIoA1yE5GCudpdV8,1320
|
7
|
-
AIEmailAutomationUtility/Process_Category.py,sha256=
|
7
|
+
AIEmailAutomationUtility/Process_Category.py,sha256=4Zrc1RaKMCK5oPYo1NfsBp1ba1G-6kMIsV0P3TLATwc,43771
|
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.27.dist-info/LICENCE.txt,sha256=2qX9IkEUBx0VJp1Vh9O2dsRwE-IpYId0lXDyn7OVsJ8,1073
|
12
|
+
AIEmailAutomationUtility-0.0.27.dist-info/METADATA,sha256=J9vR4Pa5JSL6iAWD1yZeEYBcgTGDzMimpejMr8gLKw4,637
|
13
|
+
AIEmailAutomationUtility-0.0.27.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
14
|
+
AIEmailAutomationUtility-0.0.27.dist-info/top_level.txt,sha256=3jTWrTUblVkaP7mpwY2UBSnrlfot5Ykpfsehyke-Uzw,25
|
15
|
+
AIEmailAutomationUtility-0.0.27.dist-info/RECORD,,
|
{AIEmailAutomationUtility-0.0.26.dist-info → AIEmailAutomationUtility-0.0.27.dist-info}/LICENCE.txt
RENAMED
File without changes
|
{AIEmailAutomationUtility-0.0.26.dist-info → AIEmailAutomationUtility-0.0.27.dist-info}/WHEEL
RENAMED
File without changes
|
File without changes
|