AIEmailAutomationUtility 0.0.30__py3-none-any.whl → 0.0.32__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.
@@ -10,6 +10,11 @@ import json
10
10
  import os
11
11
  import csv
12
12
 
13
+ import io
14
+ import pandas as pd
15
+ import docx
16
+ import PyPDF2
17
+
13
18
  from .Save_Transaction import Save_Transaction
14
19
  from .Email_Classification import Email_Classification
15
20
  from .EmailReplyAssistant import EmailReplyAssistant
@@ -110,7 +115,9 @@ class Email_Read:
110
115
  file_JsonArray, categories = self.read_JSON_File(templateName)
111
116
 
112
117
  except Exception as e:
118
+ login_status = "Failed"
113
119
  logger.log(f"Login failed: {e}")
120
+ raise Exception(e)
114
121
 
115
122
  # Log the result
116
123
  self.log_email_login(user_id, reciever_email_addr, Model_Name, login_status)
@@ -248,6 +255,7 @@ class Email_Read:
248
255
 
249
256
  except Exception as e:
250
257
  logger.log(f"Login failed: {e}")
258
+ return f"Login failed: {e}"
251
259
 
252
260
  # Log the result
253
261
  self.log_email_login(user_id, reciever_email_addr, Model_Name, login_status)
@@ -294,7 +302,23 @@ class Email_Read:
294
302
 
295
303
  if content_type == "text/plain":
296
304
  email_body += part.get_payload(decode=True).decode('utf-8', errors='replace')
297
-
305
+
306
+
307
+ # For attachment
308
+ if msg.is_multipart():
309
+ for part in msg.walk():
310
+ content_disposition = str(part.get("Content-Disposition") or "")
311
+ content_type = part.get_content_type()
312
+ if "attachment" in content_disposition.lower():
313
+ filename = part.get_filename() or "attachment"
314
+ content_bytes = part.get_payload(decode=True)
315
+ if content_bytes:
316
+ extracted_content = self.Extract_attachment_content(filename, content_bytes)
317
+ extracted_content =f"\n\n--- The content of the attachment '{filename}' is below ---\n{extracted_content}\n"
318
+ # email_body += f"\n\n--- The content of the attachment '{filename}' is below ---\n{extracted_content}\n"
319
+ else:
320
+ extracted_content="NA"
321
+
298
322
  else:
299
323
  email_body = msg.get_payload(decode=True).decode('utf-8', errors='replace')
300
324
  content_type = msg.get_content_type()
@@ -348,7 +372,8 @@ class Email_Read:
348
372
  "uid": uid,
349
373
  "to_email_addr": to_email_addr,
350
374
  "user_id": user_id,
351
- "is_html": is_html
375
+ "is_html": is_html,
376
+ "extracted_content" : extracted_content
352
377
  }
353
378
  processcategory = Process_Category()
354
379
  processcategory.process_cat(emailCategory, dataValues)
@@ -554,7 +579,7 @@ class Email_Read:
554
579
 
555
580
  def log_email_login(self, user_id, email, model_name, login_status):
556
581
  base_dir="EMail_log"
557
- timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
582
+ timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
558
583
  log_dir = os.path.join(base_dir, user_id)
559
584
  os.makedirs(log_dir, exist_ok=True)
560
585
  log_file_path = os.path.join(log_dir, f"{user_id}.csv")
@@ -573,5 +598,41 @@ class Email_Read:
573
598
  def read_status(self):
574
599
  global shared_status
575
600
  return shared_status
601
+
602
+ def Extract_attachment_content(self,filename, content_bytes):
603
+ extension = os.path.splitext(filename)[1].lower()
604
+
605
+ try:
606
+ if extension == ".pdf":
607
+ # Extract text from PDF
608
+ reader = PyPDF2.PdfReader(io.BytesIO(content_bytes))
609
+ text = "\n".join(page.extract_text() or "" for page in reader.pages)
610
+ return text if text.strip() else "[No text content found in PDF]"
611
+
612
+ elif extension in [".docx", ".doc"]:
613
+ # Extract text from Word document
614
+ doc = docx.Document(io.BytesIO(content_bytes))
615
+ text = "\n".join([para.text for para in doc.paragraphs])
616
+ return text if text.strip() else "[No text content found in Word document]"
617
+
618
+ elif extension in [".xls", ".xlsx"]:
619
+ # Extract text from Excel sheet
620
+ df = pd.read_excel(io.BytesIO(content_bytes), engine='openpyxl')
621
+ return df.to_string(index=False)
622
+
623
+ elif extension == ".csv":
624
+ # Extract text from CSV file
625
+ df = pd.read_csv(io.BytesIO(content_bytes))
626
+ return df.to_string(index=False)
627
+
628
+ elif extension in [".txt"]:
629
+ # Decode plain text file
630
+ return content_bytes.decode('utf-8', errors='replace')
631
+
632
+ else:
633
+ return "[Unsupported attachment type]"
634
+
635
+ except Exception as e:
636
+ return f"[Error extracting content: {str(e)}]"
576
637
 
577
638
 
@@ -106,12 +106,13 @@ class Process_Category:
106
106
  logger.log(f"document_type 108::: {document_type}")
107
107
 
108
108
  if responseMethod == "Upload_Document":
109
- # Get today's date folder path
110
- today_date = datetime.today().strftime('%Y-%m-%d')
111
- order_folder = os.path.join(document_type, today_date)
112
109
 
113
110
  email_upload_document = Email_DocumentUploader()
114
111
  if len(fileName) != 0 and document_type != '':
112
+ # Get today's date folder path
113
+ today_date = datetime.today().strftime('%Y-%m-%d')
114
+ order_folder = os.path.join(document_type, today_date)
115
+
115
116
  file_path = os.path.join(order_folder, fileName) # Correct file path
116
117
 
117
118
  with open(file_path, "rb") as file:
@@ -161,8 +162,17 @@ class Process_Category:
161
162
  # Extract customer code once
162
163
  customer_code = customer_data.get("customer_code", "")
163
164
 
165
+
164
166
  # Step 5: Identify product from email using AI
165
- products = self.identify_products(email_body, subject, Model_Name, openai_api_key, geminiAI_APIKey, localAIURL, parameters["Product_Assistant_Id"])
167
+
168
+ #If there is attachment then append the content in the body.
169
+ extracted_content = dataValues.get('extracted_content')
170
+ if extracted_content != "NA":
171
+ attachment_email_body =email_body + " \n\n" + extracted_content
172
+ products = self.identify_products(attachment_email_body, subject, Model_Name, openai_api_key, geminiAI_APIKey, localAIURL, parameters["Product_Assistant_Id"])
173
+ else:
174
+ products = self.identify_products(email_body, subject, Model_Name, openai_api_key, geminiAI_APIKey, localAIURL, parameters["Product_Assistant_Id"])
175
+
166
176
  logger.log(f'Identified Products are ::: {products}')
167
177
 
168
178
  product_determination=products
@@ -207,6 +217,8 @@ class Process_Category:
207
217
  else:
208
218
  logger.log(f"Process_Category - Quotation [0] No base price found for item '{item_no}' .")
209
219
  product["rate"] = "NA"
220
+ product["Price Pickup Source"]="NA"
221
+ product["Discount"]="NA"
210
222
  continue
211
223
 
212
224
  # Condition 1: Exact match in special_rate_customer_wise
@@ -226,6 +238,7 @@ class Process_Category:
226
238
  found_rate = True
227
239
  logger.log(f"Process_Category - Quotation [1] Special Rate for item '{item_no}' and customer '{customer_code}' is {rate}")
228
240
  price_pickup_source="SPECIAL_RATE_CUSTOMER_WISE"
241
+ Discount="NA"
229
242
 
230
243
  # Condition 2: Customer + Manufacturer discount
231
244
  if not found_rate:
@@ -241,6 +254,7 @@ class Process_Category:
241
254
 
242
255
  if discount_result:
243
256
  discount_percent = discount_result[0]
257
+ Discount= discount_percent
244
258
  rate = raw_price * (1 - int(discount_percent) / 100)
245
259
  rate = round(rate, 2)
246
260
  found_rate = True
@@ -265,6 +279,7 @@ class Process_Category:
265
279
 
266
280
  if past_sales_result:
267
281
  most_common_make, past_discount = past_sales_result
282
+ Discount=past_discount
268
283
  if isinstance(raw_price, (int, float)):
269
284
  rate = raw_price * (1 - int(past_discount) / 100)
270
285
  rate = round(rate, 2)
@@ -288,6 +303,7 @@ class Process_Category:
288
303
 
289
304
  if general_discount_result:
290
305
  general_discount_percent = general_discount_result[0]
306
+ Discount=general_discount_percent
291
307
  rate = raw_price * (1 - int(general_discount_percent) / 100)
292
308
  rate = round(rate, 2)
293
309
  found_rate = True
@@ -299,9 +315,11 @@ class Process_Category:
299
315
  rate = raw_price
300
316
  logger.log(f"Process_Category - Quotation [5] No discounts applied. Using base price for item '{item_no}': {rate}")
301
317
  price_pickup_source="PRICE_LIST"
318
+ Discount = "NA"
302
319
 
303
320
  product["rate"] = rate
304
321
  product["Price Pickup Source"]=price_pickup_source
322
+ product["Discount"]=Discount
305
323
 
306
324
  db_connection.close()
307
325
 
@@ -349,7 +367,7 @@ class Process_Category:
349
367
  mail.expunge()
350
368
  logger.log(f"Mail removed from inbox and added to '{LABEL}' label.")
351
369
 
352
- current_timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
370
+ current_timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
353
371
  filename = f'{uid}{current_timestamp}'
354
372
  logger.log(f"The file name for csv and eml is ::: {filename}")
355
373
  self.store_email_details_to_csv(
@@ -409,7 +427,7 @@ class Process_Category:
409
427
  filename = part.get_filename()
410
428
  mime_type = part.get_content_type().lower()
411
429
 
412
- if "pdf" in mime_type:
430
+ if "pdf" in mime_type or filename.lower().endswith(".pdf"):
413
431
  document_type = "Orders"
414
432
  elif "msword" in mime_type or "wordprocessingml" in mime_type or filename.lower().endswith(".docx"):
415
433
  document_type = "Order Excel"
@@ -421,12 +439,12 @@ class Process_Category:
421
439
  document_type = "Order Email"
422
440
  elif "rtf" in mime_type or filename.lower().endswith(".rtf"):
423
441
  document_type = "Order Excel"
424
-
425
- today_date = datetime.today().strftime('%Y-%m-%d') # Format: YYYY-MM-DD
426
- date_folder = os.path.join(document_type, today_date) # Path: ORDERS/YYYY-MM-DD
427
- os.makedirs(date_folder, exist_ok=True)
428
442
 
429
443
  if filename:
444
+ today_date = datetime.today().strftime('%Y-%m-%d') # Format: YYYY-MM-DD
445
+ date_folder = os.path.join(document_type, today_date) # Path: ORDERS/YYYY-MM-DD
446
+ os.makedirs(date_folder, exist_ok=True)
447
+
430
448
  filepath = os.path.join(date_folder, filename) # Save inside date-wise folder
431
449
 
432
450
  with open(filepath, 'wb') as f:
@@ -462,7 +480,7 @@ class Process_Category:
462
480
  fileName = fileName + ".txt"
463
481
  filePath = os.path.join(order_folder, fileName)
464
482
 
465
- with open(filePath, "w") as file:
483
+ with open(filePath, "w", encoding="utf-8") as file:
466
484
  file.write(text)
467
485
  logger.log(f"New TXT file created from email body and stored in '{filePath}'")
468
486
  else:
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: AIEmailAutomationUtility
3
- Version: 0.0.30
4
- Summary: Get database access from local path for any system.
3
+ Version: 0.0.32
4
+ Summary: Make changes in code, should run for windows and ubuntu
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
- Get database access from local path for any system.
16
+ Make changes in code, should run for windows and ubuntu
@@ -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=YJu4tuTHr0K-5vuds9gZfj-Hwsgm4MuAOP39Lmu_t98,3219
4
4
  AIEmailAutomationUtility/Email_Draft.py,sha256=DcyBeDaE8CReKHnHxLiz-o2tDxuUgwy91c4k0qhQbVw,7749
5
- AIEmailAutomationUtility/Email_Read.py,sha256=pRqc9295r_qqdfWIFJFG2XH9J-VlvO1E0yP3iv_cRV4,27802
5
+ AIEmailAutomationUtility/Email_Read.py,sha256=zE2dK4h0XfKWuSuMJs3E2U-yMPHzRtBfNSdP74h_pDI,30743
6
6
  AIEmailAutomationUtility/Email_Upload_Document.py,sha256=3bdkxfDlwoeRp-46KPw2Gs1dqBhEIoA1yE5GCudpdV8,1320
7
- AIEmailAutomationUtility/Process_Category.py,sha256=zijeYAIy5fj3hXmRQ_1CEPuikw-ELNolxuaEdyJj5OY,43745
7
+ AIEmailAutomationUtility/Process_Category.py,sha256=xXiXYf_dmscKO6scFOMRCWLgKhsV8RsCVK1AXPnE1DI,44695
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.30.dist-info/LICENCE.txt,sha256=2qX9IkEUBx0VJp1Vh9O2dsRwE-IpYId0lXDyn7OVsJ8,1073
12
- AIEmailAutomationUtility-0.0.30.dist-info/METADATA,sha256=pzhpoUozUqOinZaKi0dn4qEi4md36AQa4lGD3wqJq94,603
13
- AIEmailAutomationUtility-0.0.30.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
14
- AIEmailAutomationUtility-0.0.30.dist-info/top_level.txt,sha256=3jTWrTUblVkaP7mpwY2UBSnrlfot5Ykpfsehyke-Uzw,25
15
- AIEmailAutomationUtility-0.0.30.dist-info/RECORD,,
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,,