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.
@@ -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, base_dir="EMail_log"):
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
- logger.log(f"the json datavalues of mailare ------------------------ {mail}")
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: {customer_data}")
160
- logger.log(f'This is the customer data we are getting ::: {customer_data}')
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'this is the products data we are getting ::: {products}')
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}'. Skipping...")
204
- product["rate"] = None
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
- else:
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
- else:
283
- logger.log(f"[4] No applicable discount found for item '{item_no}'")
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
- cursor.close()
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
- logger.log(f"email_id: {email_id}, type: {type(email_id)}")
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"the file name is ---------------- {filename}")
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 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."""
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 only price column even if it is none(set it as -).
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
- # Prepare the data to write
773
- csv_data = [{
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 performed': action_performed,
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 generated':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
- fieldnames = ['to', 'from', 'cc', 'timestamp', 'subject', 'body', 'Category', 'Action performed', 'unique_id','status of mail draft','Response generated']
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
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: AIEmailAutomationUtility
3
- Version: 0.0.26
4
- Summary: Resolve the issue of email and excel file uploading on orders
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
- Resolve the issue of email and excel file uploading on orders
16
+ Added 3 more parameters in the right panel of quotation email utilty
@@ -1,15 +1,15 @@
1
- AIEmailAutomationUtility/EmailReplyAssistant.py,sha256=oRobM6C1AwiRA3lQ2Sh51m-FaH2PpbUEidbkOG7O9Ww,6339
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=UJDHdsr5RN_GG5OsOvUKbKziaw0a-kR3TT0iUv0oRmI,27795
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=EojfyGAN7U6NIBqinikJIauEgzz-ZQhHpzcjNr1No9M,41234
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.26.dist-info/LICENCE.txt,sha256=2qX9IkEUBx0VJp1Vh9O2dsRwE-IpYId0lXDyn7OVsJ8,1073
12
- AIEmailAutomationUtility-0.0.26.dist-info/METADATA,sha256=8n0W1uGzS7F2kkzHYPYi32axicmcCOapZ-LK8ql7fKs,623
13
- AIEmailAutomationUtility-0.0.26.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
14
- AIEmailAutomationUtility-0.0.26.dist-info/top_level.txt,sha256=3jTWrTUblVkaP7mpwY2UBSnrlfot5Ykpfsehyke-Uzw,25
15
- AIEmailAutomationUtility-0.0.26.dist-info/RECORD,,
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,,