Danialebrat commited on
Commit
ce32f1f
·
1 Parent(s): 0c9d257

- updating the system to be able to work with all brands

Browse files
Messaging_system/Message_generator.py CHANGED
@@ -181,6 +181,9 @@ class MessageGenerator:
181
  Your task is to select the best 'header' and a 'message' for a {self.Core.get_instrument()} student as a push notification.
182
  Based on the user instructions, you might need to **modify the selected option** very minimal and slightly to improve personalization if capable.
183
  **Important Note**: header < {self.Core.config_file["header_limit"]} and message < {self.Core.config_file["message_limit"]} characters.
 
 
 
184
  """
185
 
186
  else:
 
181
  Your task is to select the best 'header' and a 'message' for a {self.Core.get_instrument()} student as a push notification.
182
  Based on the user instructions, you might need to **modify the selected option** very minimal and slightly to improve personalization if capable.
183
  **Important Note**: header < {self.Core.config_file["header_limit"]} and message < {self.Core.config_file["message_limit"]} characters.
184
+
185
+ Don't use below phrases:
186
+ - Hit the high notes / Hit those notes
187
  """
188
 
189
  else:
Messaging_system/MultiMessage.py CHANGED
@@ -424,50 +424,6 @@ We have previously sent these push notifications to the user and The user has no
424
  self.Core.users_df.at[idx, k] = v
425
  return self.Core.users_df.loc[idx]
426
 
427
- # =======================================================================
428
- # def fetch_recommendation_data(self, user, message):
429
- # """
430
- # Extracts recommendation data from user's recsys_result and merges it into the given
431
- # message dictionary. Identical to single-message usage.
432
- #
433
- # :param user: The user row (with 'recsys_result', 'recommendation', etc.).
434
- # :param message: Dictionary with at least "header" and "message".
435
- # :return: Enriched dict (header, message, content_id, web_url_path, title, thumbnail_url)
436
- # """
437
- # user_id = user["user_id"]
438
- # content_id = int(user["recommendation"])
439
- # recsys_json_str = user["recsys_result"]
440
- # recsys_data = json.loads(recsys_json_str)
441
- #
442
- # # Initialize variable to store found item
443
- # found_item = None
444
- # for category, items in recsys_data.items():
445
- # for item in items:
446
- # if item.get("content_id") == content_id:
447
- # found_item = item
448
- # break
449
- # if found_item:
450
- # break
451
- #
452
- # if not found_item:
453
- # print(f"content_id {content_id} not found in recsys_data for user_id {user_id}.")
454
- # return None
455
- #
456
- # web_url_path = found_item.get("web_url_path")
457
- # title = found_item.get("title")
458
- # thumbnail_url = found_item.get("thumbnail_url")
459
- #
460
- # # Construct final dictionary
461
- # output_message = {
462
- # "header": message.get("header"),
463
- # "message": message.get("message", "").replace('\\', '').replace('"', ''),
464
- # "content_id": content_id,
465
- # "web_url_path": web_url_path,
466
- # "title": title,
467
- # "thumbnail_url": thumbnail_url
468
- # }
469
- # return output_message
470
-
471
  # --------------------------------------------------------------
472
  # --------------------------------------------------------------
473
 
@@ -484,6 +440,9 @@ We have previously sent these push notifications to the user and The user has no
484
  Your task is to select the best 'header' and a 'message' for a {self.Core.get_instrument()} student as a push notification.
485
  Based on the user instructions, you might need to **modify the selected option** very minimal and slightly to improve personalization if capable.
486
  **Important Note**: header < {self.Core.config_file["header_limit"]} and message < {self.Core.config_file["message_limit"]} characters.
 
 
 
487
  """
488
 
489
  else:
 
424
  self.Core.users_df.at[idx, k] = v
425
  return self.Core.users_df.loc[idx]
426
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
427
  # --------------------------------------------------------------
428
  # --------------------------------------------------------------
429
 
 
440
  Your task is to select the best 'header' and a 'message' for a {self.Core.get_instrument()} student as a push notification.
441
  Based on the user instructions, you might need to **modify the selected option** very minimal and slightly to improve personalization if capable.
442
  **Important Note**: header < {self.Core.config_file["header_limit"]} and message < {self.Core.config_file["message_limit"]} characters.
443
+
444
+ Don't use below phrases:
445
+ - Hit the high notes / Hit those notes
446
  """
447
 
448
  else:
Messaging_system/Permes.py CHANGED
@@ -84,6 +84,9 @@ class Permes:
84
 
85
  users_df = self._create_personalized_message(CoreConfig=personalize_message, progress_callback=progress_callback)
86
 
 
 
 
87
  total_prompt_tokens = personalize_message.total_tokens["prompt_tokens"]
88
  total_completion_tokens = personalize_message.total_tokens["completion_tokens"]
89
 
@@ -175,6 +178,10 @@ class Permes:
175
  datacollect = DataCollector(CoreConfig)
176
  CoreConfig = datacollect.gather_data()
177
 
 
 
 
 
178
  # generating recommendations for users, if we want to include recommendations in the message
179
  if CoreConfig.involve_recsys_result and CoreConfig.messaging_mode != "message":
180
  Recommender = LLMR(CoreConfig, random=True)
 
84
 
85
  users_df = self._create_personalized_message(CoreConfig=personalize_message, progress_callback=progress_callback)
86
 
87
+ if users_df is None:
88
+ return None
89
+
90
  total_prompt_tokens = personalize_message.total_tokens["prompt_tokens"]
91
  total_completion_tokens = personalize_message.total_tokens["completion_tokens"]
92
 
 
178
  datacollect = DataCollector(CoreConfig)
179
  CoreConfig = datacollect.gather_data()
180
 
181
+ if len(CoreConfig.users_df) == 0:
182
+ print("There is no user to generate messages")
183
+ return None
184
+
185
  # generating recommendations for users, if we want to include recommendations in the message
186
  if CoreConfig.involve_recsys_result and CoreConfig.messaging_mode != "message":
187
  Recommender = LLMR(CoreConfig, random=True)
Messaging_system/PromptGenerator.py CHANGED
@@ -150,14 +150,15 @@ Below is the content we want to recommend to the user:
150
  → Recommended Content Details:
151
  {user["recommendation_info"]}
152
 
153
- When incorporating this content into the message, follow these guidelines to keep the message friendly, relevant, and casual (not too scripted):
 
154
 
155
  1. **Title Usage**:
156
  - Refer to the **CONTENT_TITLE** naturally in the message — paraphrase or describe it, but do *not* quote it or use it verbatim.
157
  - Avoid making it feel like a promotion; frame it as something that *might interest* or *help* the user.
158
 
159
  2. **Content Type Context**:
160
- - Mention the **CONTENT_TYPE** (e.g., course, workout, quicktip) only if it flows naturally in the message.
161
 
162
  3. **Artist/Instructor Name**:
163
  - If the full name of the **ARTIST** is available, mention it casually if appropriate (e.g., "led by Jordan Mitchell").
@@ -254,8 +255,8 @@ Goal: Make the recommendation feel personalized and casually relevant — not ge
254
  **Expected output structure:**
255
 
256
  {{
257
- "header": "selected header from options",
258
- "message": "selected message from options",
259
  }}
260
 
261
  {general_instructions}
 
150
  → Recommended Content Details:
151
  {user["recommendation_info"]}
152
 
153
+
154
+ When incorporating this content into the message and header, follow these guidelines to keep them friendly, relevant, and casual (not too scripted):
155
 
156
  1. **Title Usage**:
157
  - Refer to the **CONTENT_TITLE** naturally in the message — paraphrase or describe it, but do *not* quote it or use it verbatim.
158
  - Avoid making it feel like a promotion; frame it as something that *might interest* or *help* the user.
159
 
160
  2. **Content Type Context**:
161
+ - Mention the **CONTENT_TYPE** (e.g., course, workout, lesson) only if it flows naturally in the message.
162
 
163
  3. **Artist/Instructor Name**:
164
  - If the full name of the **ARTIST** is available, mention it casually if appropriate (e.g., "led by Jordan Mitchell").
 
255
  **Expected output structure:**
256
 
257
  {{
258
+ "header": "output header considering instructions",
259
+ "message": "output message considering instructions",
260
  }}
261
 
262
  {general_instructions}
Messaging_system/SnowFlakeConnection.py CHANGED
@@ -243,6 +243,12 @@ class SnowFlakeConn:
243
  def close_connection(self):
244
  self.session.close()
245
 
 
 
 
 
 
 
246
  # ===============================================================
247
  def get_users_in_campaign(self, brand, campaign_name="Singeo - Inactive Members (for 3 days) - Re-engagement", stage=1, campaign_view='singeo_re_engagement'):
248
  """
@@ -354,4 +360,33 @@ JOIN latest_msg l
354
  """
355
 
356
  users_df = self.run_read_query(query, data=f"{brand}_campaign")
357
- return users_df
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
243
  def close_connection(self):
244
  self.session.close()
245
 
246
+ def get_inactive_users_by_brand(self, brand):
247
+ if brand == "singeo":
248
+ return self.get_users_in_campaign(brand=brand)
249
+ else:
250
+ return self._get_inactive_users(brand=brand)
251
+
252
  # ===============================================================
253
  def get_users_in_campaign(self, brand, campaign_name="Singeo - Inactive Members (for 3 days) - Re-engagement", stage=1, campaign_view='singeo_re_engagement'):
254
  """
 
360
  """
361
 
362
  users_df = self.run_read_query(query, data=f"{brand}_campaign")
363
+ return users_df
364
+
365
+ def _get_inactive_users(self, brand: str):
366
+ """
367
+ Return up to 50 USER_IDs in the requested brand whose last interaction is > 5 days ago,
368
+ restricted to users that exist in ONLINE_RECSYS.PREPROCESSED.USERS.
369
+ """
370
+ query = f"""
371
+ WITH last_touch AS (
372
+ SELECT
373
+ USER_ID,
374
+ MAX(TIMESTAMP) AS last_interaction_at
375
+ FROM ONLINE_RECSYS.PREPROCESSED.RECSYS_INTEACTIONS
376
+ WHERE BRAND = '{brand}'
377
+ GROUP BY USER_ID
378
+ )
379
+ SELECT lt.USER_ID
380
+ FROM last_touch lt
381
+ INNER JOIN ONLINE_RECSYS.PREPROCESSED.USERS u
382
+ ON u.USER_ID = lt.USER_ID
383
+ AND u.BRAND = '{brand}'
384
+ WHERE lt.last_interaction_at < DATEADD('day', -5, CURRENT_TIMESTAMP())
385
+ ORDER BY lt.last_interaction_at ASC
386
+ LIMIT 50;
387
+ """
388
+
389
+ users_df = self.run_read_query(query, data=f"{brand}_campaign")
390
+ return users_df
391
+
392
+
app.py CHANGED
@@ -161,7 +161,7 @@ with st.sidebar:
161
  st.session_state.session = Session.builder.configs(conn).create()
162
 
163
  snowflake = SnowFlakeConn(session=st.session_state.session, brand=st.session_state.brand)
164
- st.session_state.data = snowflake.get_users_in_campaign(brand=st.session_state.brand)
165
  st.success("File loaded!")
166
 
167
  st.markdown("---")
@@ -297,6 +297,10 @@ with tab2:
297
  personalization=st.session_state.personalization
298
  )
299
 
 
 
 
 
300
  # ─ cache output
301
  st.session_state.users_message = df_msg
302
  st.session_state.csv_output = df_msg.to_csv(
 
161
  st.session_state.session = Session.builder.configs(conn).create()
162
 
163
  snowflake = SnowFlakeConn(session=st.session_state.session, brand=st.session_state.brand)
164
+ st.session_state.data = snowflake.get_inactive_users_by_brand(brand=st.session_state.brand)
165
  st.success("File loaded!")
166
 
167
  st.markdown("---")
 
297
  personalization=st.session_state.personalization
298
  )
299
 
300
+ if df_msg is None:
301
+ st.write("##### 👤 There were no eligible user to generate messages. Consider setting higher number of users.")
302
+
303
+
304
  # ─ cache output
305
  st.session_state.users_message = df_msg
306
  st.session_state.csv_output = df_msg.to_csv(