Shageenderan Sapai commited on
Commit
a7684ad
·
1 Parent(s): 0426bd3

Align with Staging

Browse files
Files changed (3) hide show
  1. app/main.py +65 -12
  2. app/user.py +17 -1
  3. app/utils.py +143 -154
app/main.py CHANGED
@@ -15,7 +15,7 @@ from openai import OpenAI
15
  import psycopg2
16
  from psycopg2 import sql
17
  import os
18
- from app.utils import add_to_cache, get_api_key, get_user_info, get_growth_guide_session, pop_cache, print_log, get_user, upload_mementos_to_db, get_user_summary, get_user_life_status, get_life_score
19
  from dotenv import load_dotenv
20
  import logging.config
21
  import time
@@ -40,6 +40,20 @@ else:
40
  for file in os.listdir(os.path.join('users', 'data')):
41
  os.remove(os.path.join('users', 'data', file))
42
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
  if not os.path.exists(os.path.join('users', 'to_upload')):
44
  os.makedirs(os.path.join('users', 'to_upload'))
45
  if not os.path.exists(os.path.join('mementos', 'to_upload')):
@@ -254,6 +268,10 @@ class ErrorResponse(BaseModel):
254
  message: str
255
  timestamp: datetime = datetime.now()
256
 
 
 
 
 
257
  @app.get("/ok")
258
  def ok_endpoint():
259
  print_log("INFO", "health check endpoint")
@@ -382,6 +400,7 @@ def migrate_user(request: CreateUserItem, api_key: str = Security(get_api_key)):
382
  api_response['challenges'] = user.challenges
383
  api_response['other_focusses'] = user.other_focusses
384
  api_response['scores'] = f"Personal Growth: {user.personal_growth_score} || Career: {user.career_growth_score} || Health/Wellness: {user.health_and_wellness_score} || Relationships: {user.relationship_score} || Mental Health: {user.mental_well_being_score}"
 
385
 
386
  add_to_cache(user)
387
  pop_cache(user.user_id)
@@ -424,7 +443,7 @@ def get_user_by_id(user_id: str, api_key: str = Security(get_api_key)):
424
  api_response['other_focusses'] = user.other_focusses
425
  api_response['reminders'] = user.reminders
426
  api_response['scores'] = f"Personal Growth: {user.personal_growth_score} || Career: {user.career_growth_score} || Health/Wellness: {user.health_and_wellness_score} || Relationships: {user.relationship_score} || Mental Health: {user.mental_well_being_score}"
427
-
428
 
429
  return api_response
430
  except LookupError:
@@ -664,10 +683,12 @@ def chat(request: ChatItem, api_key: str = Security(get_api_key)):
664
  if recent_run:
665
  user.cancel_run(recent_run)
666
  response = user.send_message(user.get_recent_message())
667
-
668
- print_log("INFO",f"Assistant: {response['content']}", extra={"user_id": request.user_id, "endpoint": "/chat"})
669
- logger.info(f"Assistant: {response['content']}", extra={"user_id": request.user_id, "endpoint": "/chat"})
670
- return {"response": response}
 
 
671
  except LookupError:
672
  print_log("ERROR",f"User not found for chat: {request.user_id}", extra={"user_id": request.user_id, "endpoint": "/chat"})
673
  logger.error(f"User not found for chat: {request.user_id}", extra={"user_id": request.user_id, "endpoint": "/chat"})
@@ -929,12 +950,44 @@ def get_life_status_by_id(user_id: str, api_key: str = Security(get_api_key)):
929
 
930
  @app.post("/add_booking_point")
931
  def add_booking_point_by_user(user_id: str, api_key: str = Security(get_api_key)):
932
- user = get_user(user_id)
933
- user.add_point_for_booking()
934
- return {"response": "ok"}
 
 
 
 
 
 
 
 
935
 
936
  @app.post("/add_session_completion_point")
937
  def add_session_completion_point_by_user(user_id: str, api_key: str = Security(get_api_key)):
938
- user = get_user(user_id)
939
- user.add_point_for_completing_session()
940
- return {"response": "ok"}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
  import psycopg2
16
  from psycopg2 import sql
17
  import os
18
+ from app.utils import add_to_cache, get_api_key, get_user_info, get_growth_guide_session, pop_cache, print_log, get_user, upload_mementos_to_db, get_user_summary, get_user_life_status, create_pre_gg_report, get_life_score
19
  from dotenv import load_dotenv
20
  import logging.config
21
  import time
 
40
  for file in os.listdir(os.path.join('users', 'data')):
41
  os.remove(os.path.join('users', 'data', file))
42
 
43
+ if not os.path.exists(os.path.join('bookings', 'data')):
44
+ os.makedirs(os.path.join('bookings', 'data'))
45
+ else:
46
+ # Folder exists, we want to clear all current booking data
47
+ for file in os.listdir(os.path.join('bookings', 'data')):
48
+ os.remove(os.path.join('bookings', 'data', file))
49
+
50
+ if not os.path.exists(os.path.join('bookings', 'to_upload')):
51
+ os.makedirs(os.path.join('bookings', 'to_upload'))
52
+ else:
53
+ # Folder exists, we want to clear all current booking data
54
+ for file in os.listdir(os.path.join('bookings', 'to_upload')):
55
+ os.remove(os.path.join('bookings', 'to_upload', file))
56
+
57
  if not os.path.exists(os.path.join('users', 'to_upload')):
58
  os.makedirs(os.path.join('users', 'to_upload'))
59
  if not os.path.exists(os.path.join('mementos', 'to_upload')):
 
268
  message: str
269
  timestamp: datetime = datetime.now()
270
 
271
+ class BookingItem(BaseModel):
272
+ booking_id: str
273
+
274
+
275
  @app.get("/ok")
276
  def ok_endpoint():
277
  print_log("INFO", "health check endpoint")
 
400
  api_response['challenges'] = user.challenges
401
  api_response['other_focusses'] = user.other_focusses
402
  api_response['scores'] = f"Personal Growth: {user.personal_growth_score} || Career: {user.career_growth_score} || Health/Wellness: {user.health_and_wellness_score} || Relationships: {user.relationship_score} || Mental Health: {user.mental_well_being_score}"
403
+ api_response['recent_wins'] = user.recent_wins
404
 
405
  add_to_cache(user)
406
  pop_cache(user.user_id)
 
443
  api_response['other_focusses'] = user.other_focusses
444
  api_response['reminders'] = user.reminders
445
  api_response['scores'] = f"Personal Growth: {user.personal_growth_score} || Career: {user.career_growth_score} || Health/Wellness: {user.health_and_wellness_score} || Relationships: {user.relationship_score} || Mental Health: {user.mental_well_being_score}"
446
+ api_response['recent_wins'] = user.recent_wins
447
 
448
  return api_response
449
  except LookupError:
 
683
  if recent_run:
684
  user.cancel_run(recent_run)
685
  response = user.send_message(user.get_recent_message())
686
+
687
+ finally:
688
+ print_log("INFO",f"Assistant: {response['content']}", extra={"user_id": request.user_id, "endpoint": "/chat"})
689
+ logger.info(f"Assistant: {response['content']}", extra={"user_id": request.user_id, "endpoint": "/chat"})
690
+
691
+ return {"response": response}
692
  except LookupError:
693
  print_log("ERROR",f"User not found for chat: {request.user_id}", extra={"user_id": request.user_id, "endpoint": "/chat"})
694
  logger.error(f"User not found for chat: {request.user_id}", extra={"user_id": request.user_id, "endpoint": "/chat"})
 
950
 
951
  @app.post("/add_booking_point")
952
  def add_booking_point_by_user(user_id: str, api_key: str = Security(get_api_key)):
953
+ try:
954
+ user = get_user(user_id)
955
+ user.add_point_for_booking()
956
+ return {"response": "ok"}
957
+ except Exception as e:
958
+ print_log("ERROR",f"Error: {str(e)}", extra={"user_id": user_id, "endpoint": "/add_booking_point"}, exc_info=True)
959
+ logger.error(f"Error: {str(e)}", extra={"user_id": user_id, "endpoint": "/add_booking_point"}, exc_info=True)
960
+ raise HTTPException(
961
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
962
+ detail=str(e)
963
+ )
964
 
965
  @app.post("/add_session_completion_point")
966
  def add_session_completion_point_by_user(user_id: str, api_key: str = Security(get_api_key)):
967
+ try:
968
+ user = get_user(user_id)
969
+ user.add_point_for_completing_session()
970
+ return {"response": "ok"}
971
+
972
+
973
+
974
+ except Exception as e:
975
+ print_log("ERROR",f"Error: {str(e)}", extra={"user_id": user_id, "endpoint": "/add_session_completion_point"}, exc_info=True)
976
+ logger.error(f"Error: {str(e)}", extra={"user_id": user_id, "endpoint": "/add_session_completion_point"}, exc_info=True)
977
+ raise HTTPException(
978
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
979
+ detail=str(e)
980
+ )
981
+
982
+ @app.post("/create_pre_gg_report")
983
+ def create_pre_gg_by_booking(request: BookingItem, api_key: str = Security(get_api_key)):
984
+ try:
985
+ create_pre_gg_report(request.booking_id)
986
+ return {"response": "ok"}
987
+ except Exception as e:
988
+ print_log("ERROR",f"Error: {str(e)}", extra={"booking_id": request.booking_id, "endpoint": "/create_pre_gg_report"}, exc_info=True)
989
+ logger.error(f"Error: {str(e)}", extra={"booking_id": request.booking_id, "endpoint": "/create_pre_gg_report"}, exc_info=True)
990
+ raise HTTPException(
991
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
992
+ detail=str(e)
993
+ )
app/user.py CHANGED
@@ -298,6 +298,7 @@ class User:
298
  self.mental_well_being_score = 0
299
  self.health_and_wellness_score = 0
300
  self.reminders = None
 
301
 
302
  # Read growth_plan.json and store it
303
  # growth_plan = {"growthPlan": [
@@ -392,6 +393,13 @@ class User:
392
  self.user_interaction_guidelines = self.generate_user_interaction_guidelines(user_info, client)
393
  self.conversations = ConversationManager(client, self, asst_id)
394
 
 
 
 
 
 
 
 
395
  def add_life_score_point(self, variable, points_added, notes):
396
  if variable == 'Personal Growth':
397
  self.personal_growth_score += points_added
@@ -439,6 +447,7 @@ class User:
439
  if completed:
440
  self.update_goal(current_goal, "COMPLETED")
441
  self.add_life_score_point(variable = goal_area, points_added = 30, notes = "Completing a Goal")
 
442
 
443
  if current_goal is None:
444
  new_goal = UserDataItem(role="assistant", content=goal, area=goal_area, user_id=self.user_id, status="ONGOING", created_at=pd.Timestamp.now().strftime("%d-%m-%Y %a %H:%M:%S"), updated_at=pd.Timestamp.now().strftime("%d-%m-%Y %a %H:%M:%S"))
@@ -451,6 +460,7 @@ class User:
451
  new_goal = UserDataItem(role="assistant", content=goal, area=goal_area, user_id=self.user_id, status="ONGOING", created_at=pd.Timestamp.now().strftime("%d-%m-%Y %a %H:%M:%S"), updated_at=pd.Timestamp.now().strftime("%d-%m-%Y %a %H:%M:%S"))
452
  self.goal.append(new_goal)
453
  self.add_life_score_point(variable = goal_area, points_added = 10, notes = "Setting a Goal")
 
454
  else:
455
  self.update_goal(current_goal, "ONGOING", content=goal)
456
 
@@ -832,8 +842,10 @@ class User:
832
  ## ADD POINT FOR CHANGE DATE
833
  if self.growth_plan.current()['day'] == 7:
834
  self.add_life_score_point(variable = self.get_current_goal(full=True).area, points_added = 5, notes = "Reaching Day 7")
 
835
  elif self.growth_plan.current()['day'] == 14:
836
  self.add_life_score_point(variable = self.get_current_goal(full=True).area, points_added = 10, notes = "Reaching Day 14")
 
837
 
838
  logger.info(f"Today's action is {action}", extra={"user_id": self.user_id, "endpoint": "user_change_date"})
839
 
@@ -1073,17 +1085,21 @@ class User:
1073
 
1074
  if (num_of_micro_actions_completed in (1,3,5)) or (num_of_micro_actions_completed % 10 == 0 and num_of_micro_actions_completed != 0):
1075
  self.add_life_score_point(variable = self.get_current_goal(full=True).area, points_added = 10, notes = f"Completing the {num_of_micro_actions_completed}-th micro-action")
1076
-
 
1077
  def trigger_deep_reflection_point(self, area_of_deep_reflection):
1078
  if len(area_of_deep_reflection)>0:
1079
  for area in area_of_deep_reflection:
1080
  self.add_life_score_point(variable = area, points_added = 5, notes = f"Doing a deep reflection about {area}")
 
1081
 
1082
  def add_point_for_booking(self):
1083
  self.add_life_score_point(variable = self.get_current_goal(full=True).area, points_added = 5, notes = "Booking a GG session")
 
1084
 
1085
  def add_point_for_completing_session(self):
1086
  self.add_life_score_point(variable = self.get_current_goal(full=True).area, points_added = 20, notes = "Completing a GG session")
 
1087
 
1088
  def build_ourcoach_report(self, overview, action_plan, gg_session_notes):
1089
  logger.info(f"Building ourcoach report", extra={"user_id": self.user_id, "endpoint": "build_ourcoach_report"})
 
298
  self.mental_well_being_score = 0
299
  self.health_and_wellness_score = 0
300
  self.reminders = None
301
+ self.recent_wins = []
302
 
303
  # Read growth_plan.json and store it
304
  # growth_plan = {"growthPlan": [
 
393
  self.user_interaction_guidelines = self.generate_user_interaction_guidelines(user_info, client)
394
  self.conversations = ConversationManager(client, self, asst_id)
395
 
396
+ def add_recent_wins(self, wins):
397
+ if len(self.recent_wins)<5:
398
+ self.recent_wins.insert(0,wins)
399
+ else:
400
+ self.recent_wins.pop()
401
+ self.recent_wins.insert(0,wins)
402
+
403
  def add_life_score_point(self, variable, points_added, notes):
404
  if variable == 'Personal Growth':
405
  self.personal_growth_score += points_added
 
447
  if completed:
448
  self.update_goal(current_goal, "COMPLETED")
449
  self.add_life_score_point(variable = goal_area, points_added = 30, notes = "Completing a Goal")
450
+ self.add_recent_wins(wins = "You have completed your goal!")
451
 
452
  if current_goal is None:
453
  new_goal = UserDataItem(role="assistant", content=goal, area=goal_area, user_id=self.user_id, status="ONGOING", created_at=pd.Timestamp.now().strftime("%d-%m-%Y %a %H:%M:%S"), updated_at=pd.Timestamp.now().strftime("%d-%m-%Y %a %H:%M:%S"))
 
460
  new_goal = UserDataItem(role="assistant", content=goal, area=goal_area, user_id=self.user_id, status="ONGOING", created_at=pd.Timestamp.now().strftime("%d-%m-%Y %a %H:%M:%S"), updated_at=pd.Timestamp.now().strftime("%d-%m-%Y %a %H:%M:%S"))
461
  self.goal.append(new_goal)
462
  self.add_life_score_point(variable = goal_area, points_added = 10, notes = "Setting a Goal")
463
+ self.add_recent_wins(wins = "You have set your first goal!")
464
  else:
465
  self.update_goal(current_goal, "ONGOING", content=goal)
466
 
 
842
  ## ADD POINT FOR CHANGE DATE
843
  if self.growth_plan.current()['day'] == 7:
844
  self.add_life_score_point(variable = self.get_current_goal(full=True).area, points_added = 5, notes = "Reaching Day 7")
845
+ self.add_recent_wins(wins = "You have reached Day 7 of your growth journey!")
846
  elif self.growth_plan.current()['day'] == 14:
847
  self.add_life_score_point(variable = self.get_current_goal(full=True).area, points_added = 10, notes = "Reaching Day 14")
848
+ self.add_recent_wins(wins = "You have finished your growth journey!")
849
 
850
  logger.info(f"Today's action is {action}", extra={"user_id": self.user_id, "endpoint": "user_change_date"})
851
 
 
1085
 
1086
  if (num_of_micro_actions_completed in (1,3,5)) or (num_of_micro_actions_completed % 10 == 0 and num_of_micro_actions_completed != 0):
1087
  self.add_life_score_point(variable = self.get_current_goal(full=True).area, points_added = 10, notes = f"Completing the {num_of_micro_actions_completed}-th micro-action")
1088
+ self.add_recent_wins(wins = "You have completed a micro action!")
1089
+
1090
  def trigger_deep_reflection_point(self, area_of_deep_reflection):
1091
  if len(area_of_deep_reflection)>0:
1092
  for area in area_of_deep_reflection:
1093
  self.add_life_score_point(variable = area, points_added = 5, notes = f"Doing a deep reflection about {area}")
1094
+ self.add_recent_wins(wins = f"You have done a deep reflection about your {area}!")
1095
 
1096
  def add_point_for_booking(self):
1097
  self.add_life_score_point(variable = self.get_current_goal(full=True).area, points_added = 5, notes = "Booking a GG session")
1098
+ self.add_recent_wins(wins = "You have booked a Growth Guide session!")
1099
 
1100
  def add_point_for_completing_session(self):
1101
  self.add_life_score_point(variable = self.get_current_goal(full=True).area, points_added = 20, notes = "Completing a GG session")
1102
+ self.add_recent_wins(wins = "You have completed a Growth Guide session!")
1103
 
1104
  def build_ourcoach_report(self, overview, action_plan, gg_session_notes):
1105
  logger.info(f"Building ourcoach report", extra={"user_id": self.user_id, "endpoint": "build_ourcoach_report"})
app/utils.py CHANGED
@@ -22,8 +22,11 @@ from cachetools import TTLCache
22
  import threading
23
  import time
24
  import uuid
25
-
26
  from app.cache import CustomTTLCache, upload_file_to_s3
 
 
 
 
27
 
28
  load_dotenv()
29
 
@@ -96,31 +99,8 @@ def get_user(user_id):
96
  raise ReferenceError(f"User {user_id} pickle still being created")
97
  raise LookupError(f"User [{user_id}] has not onboarded yet")
98
 
99
- def get_life_score(user_id):
100
- function_name = get_life_score.__name__
101
- logger.info(f"Generating user life score for user {user_id}", extra={'user_id': user_id, 'endpoint': function_name})
102
-
103
- # Step 1: Call get_user to get user's info
104
- try:
105
- user = get_user(user_id)
106
- # user_info = user.user_info
107
- # user_messages = user.get_messages()
108
- ## all the life scores here
109
-
110
- life_score = {
111
- "personal_growth_score": user.personal_growth_score,
112
- "career_growth_score": user.career_growth_score,
113
- "relationship_score": user.relationship_score,
114
- "mental_well_being_score": user.mental_well_being_score,
115
- "health_and_wellness_score": user.health_and_wellness_score
116
- }
117
- except LookupError as e:
118
- logger.error(f"Error fetching user data: {e}", extra={'user_id': user_id, 'endpoint': function_name})
119
- raise e
120
-
121
- return life_score
122
-
123
- def generate_html(json_data, coach_name='Growth Guide'):
124
  data = json_data["pre_growth_guide_session_report"]
125
  user_overview = data["user_overview"]
126
  personality_insights = data["personality_insights"]
@@ -290,7 +270,69 @@ def generate_html(json_data, coach_name='Growth Guide'):
290
  </body>
291
  </html>
292
  '''
293
- return html_content
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
294
 
295
  def get_user_summary(user_id):
296
  function_name = get_user_summary.__name__
@@ -769,6 +811,53 @@ def get_user_summary(user_id):
769
  logger.info(f"User summary generated successfully for user {user_id}", extra={'user_id': user_id, 'endpoint': function_name})
770
  return reports
771
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
772
  def get_user_life_status(user_id):
773
  function_name = get_user_life_status.__name__
774
  logger.info(f"Generating user life status for user {user_id}", extra={'user_id': user_id, 'endpoint': function_name})
@@ -787,72 +876,30 @@ def get_user_life_status(user_id):
787
  [f"{message['role'].capitalize()}: {message['content']}" for message in user_messages]
788
  )
789
 
 
790
  # Build the system prompt according to the provided instructions
791
  system_prompt = """
792
  You are an AI assistant that generates a personalized life status report for users based on their profile and chat history. Your task is to analyze the provided user data and produce a JSON output following the specified schema.
793
 
794
  **Instructions:**
795
 
796
- 1. **Life Score:**
797
- - Calculate numerical scores for each of the following areas based on the user's profile and chat history, especially their responses to quantitative questions (e.g., "on a scale of 1 to 5, how do you feel about ..."):
798
- - `personal_growth`
799
- - `career_growth`
800
- - `mental_well_being`
801
- - `health_and_wellness`
802
- - `relationship`
803
- - Each score should be a float between 0 and 1. If an area hasn't been covered in the chat history, assign it a score of 0.
804
- - Compute the `overall` score as the average of the non-zero area scores.
805
-
806
- 2. **Mantra of the Week:**
807
  - Create a very short encouragement quote that encapsulates the user's journey toward achieving their goals.
808
  - The mantra **MUST** be a single sentence with fewer than 5 words.
809
 
810
- 3. **This Week's Focus:**
811
- - Identify the top three most important areas for the user from the five key areas:
812
- - `personal_growth`
813
- - `career_growth`
814
- - `mental_well_being`
815
- - `health_and_wellness`
816
- - `relationship`
817
- - For each selected area, provide a concise statement outlining the user's focus in that area.
818
-
819
- 4. **Suggested Action Items:**
820
- - For the same three areas identified above, suggest concrete and actionable items that the user can undertake to make progress.
821
-
822
  **Output Format:**
823
 
824
  Produce your response in JSON format adhering to the following schema:
825
 
826
  ```json
827
  {
828
- "life_score": {
829
- "overall": float,
830
- "personal_growth": float,
831
- "health_and_wellness": float,
832
- "mental_well_being": float,
833
- "career_growth": float,
834
- "relationship": float
835
- },
836
- "mantra_of_the_week": str,
837
- "this_week_focus": [
838
- {"area": str, "focus": str},
839
- {"area": str, "focus": str},
840
- {"area": str, "focus": str}
841
- ],
842
- "action_items": [
843
- {"area": str, "focus": str},
844
- {"area": str, "focus": str},
845
- {"area": str, "focus": str}
846
- ]
847
  }
848
  ```
849
 
850
  **Guidelines:**
851
 
852
- - Ensure all numerical values are floats (e.g., 3.0 instead of 3).
853
  - The `mantra_of_the_week` should be personalized, positive, and encouraging. It **MUST** be a single sentence with fewer than 5 words.
854
- - Focus statements and action items should be clear, specific, and tailored to the user's context.
855
- - Do not include any additional text or commentary outside of the JSON structure.
856
  """
857
 
858
  # Combine user information and chat history for context
@@ -899,93 +946,13 @@ def get_user_life_status(user_id):
899
  "schema": {
900
  "type": "object",
901
  "properties": {
902
- "life_score": {
903
- "type": "object",
904
- "description": "Numerical life scores across different areas.",
905
- "properties": {
906
- "overall": {
907
- "type": "number",
908
- "description": "Overall life score, average of all area scores."
909
- },
910
- "personal_growth": {
911
- "type": "number",
912
- "description": "Life score for personal growth."
913
- },
914
- "health_and_wellness": {
915
- "type": "number",
916
- "description": "Life score for health and wellness."
917
- },
918
- "mental_well_being": {
919
- "type": "number",
920
- "description": "Life score for mental well-being."
921
- },
922
- "career_growth": {
923
- "type": "number",
924
- "description": "Life score for career growth."
925
- },
926
- "relationship": {
927
- "type": "number",
928
- "description": "Life score for relationships."
929
- }
930
- },
931
- "required": [
932
- "overall",
933
- "personal_growth",
934
- "health_and_wellness",
935
- "mental_well_being",
936
- "career_growth",
937
- "relationship"
938
- ],
939
- "additionalProperties": False
940
- },
941
  "mantra_of_the_week": {
942
  "type": "string",
943
  "description": "A very short encouragement quote that encapsulates the user's journey to achieve their goals."
944
- },
945
- "this_week_focus": {
946
- "type": "array",
947
- "description": "List of the user's top 3 most important areas with focus.",
948
- "items": {
949
- "type": "object",
950
- "properties": {
951
- "area": {
952
- "type": "string",
953
- "description": "The area of focus (e.g., 'personal_growth')."
954
- },
955
- "focus": {
956
- "type": "string",
957
- "description": "The focus within that area."
958
- }
959
- },
960
- "required": ["area", "focus"],
961
- "additionalProperties": False
962
- }
963
- },
964
- "action_items": {
965
- "type": "array",
966
- "description": "List of suggested concrete action items for the user.",
967
- "items": {
968
- "type": "object",
969
- "properties": {
970
- "area": {
971
- "type": "string",
972
- "description": "The area of action (e.g., 'personal_growth')."
973
- },
974
- "focus": {
975
- "type": "string",
976
- "description": "Concrete action items in that area."
977
- }
978
- },
979
- "required": ["area", "focus"],
980
- "additionalProperties": False
981
- }
982
  }
983
  },
984
  "required": [
985
- "life_score",
986
- "mantra_of_the_week",
987
- "this_week_focus",
988
- "action_items"
989
  ],
990
  "additionalProperties": False
991
  }
@@ -1000,12 +967,34 @@ def get_user_life_status(user_id):
1000
  )
1001
 
1002
  # Get response and convert into dictionary
1003
- reports = json.loads(response.choices[0].message.content)
1004
 
1005
  except Exception as e:
1006
  logger.error(f"OpenAI API call failed: {e}", extra={'user_id': user_id, 'endpoint': function_name})
1007
  raise e
1008
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1009
  # Step 4: Return the JSON reports
1010
  logger.info(f"User life status generated successfully for user {user_id}", extra={'user_id': user_id, 'endpoint': function_name})
1011
  return reports
 
22
  import threading
23
  import time
24
  import uuid
 
25
  from app.cache import CustomTTLCache, upload_file_to_s3
26
+ import pdfkit
27
+ import PyPDF2
28
+ import secrets
29
+ import string
30
 
31
  load_dotenv()
32
 
 
99
  raise ReferenceError(f"User {user_id} pickle still being created")
100
  raise LookupError(f"User [{user_id}] has not onboarded yet")
101
 
102
+ def generate_html(json_data, coach_name='Growth Guide', booking_id = None):
103
+ function_name = generate_html.__name__
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
104
  data = json_data["pre_growth_guide_session_report"]
105
  user_overview = data["user_overview"]
106
  personality_insights = data["personality_insights"]
 
270
  </body>
271
  </html>
272
  '''
273
+
274
+ file_path = os.path.join("bookings", "data",f"{booking_id}.html")
275
+ path_to_upload = os.path.join("bookings", "to_upload",f"{booking_id}.pdf")
276
+ password = "Ourcoach2024!"
277
+
278
+ ## SAVING HTML FILE
279
+ try:
280
+ # Open the file in write mode
281
+ with open(file_path, 'w', encoding='utf-8') as html_file:
282
+ html_file.write(html_content)
283
+ logger.info(f"File '{booking_id}.html' has been created successfully.", extra={'booking_id': booking_id, 'endpoint': function_name})
284
+
285
+ # Saving as PDF File
286
+ pdfkit.from_file(file_path, path_to_upload, options={'encoding': 'UTF-8'})
287
+ logger.info(f"File '{booking_id}.pdf' has been created successfully.", extra={'booking_id': booking_id, 'endpoint': function_name})
288
+
289
+ ## ENCRYPTING PDF
290
+ logger.info(f"Encrypting '{booking_id}.pdf'...", extra={'booking_id': booking_id, 'endpoint': function_name})
291
+ with open(path_to_upload, 'rb') as file:
292
+ pdf_reader = PyPDF2.PdfReader(file)
293
+ pdf_writer = PyPDF2.PdfWriter()
294
+
295
+ # Add all pages to the writer
296
+ for page_num in range(len(pdf_reader.pages)):
297
+ pdf_writer.add_page(pdf_reader.pages[page_num])
298
+
299
+ # Encrypt the PDF with the given password
300
+ pdf_writer.encrypt(password)
301
+
302
+ with open(path_to_upload, 'wb') as encrypted_file:
303
+ pdf_writer.write(encrypted_file)
304
+
305
+ logger.info(f"Succesfully encrypted '{booking_id}.pdf'", extra={'booking_id': booking_id, 'endpoint': function_name})
306
+
307
+ except Exception as e:
308
+ logger.error(f"An error occurred: {e}", extra={'booking_id': booking_id, 'endpoint': function_name})
309
+ raise
310
+
311
+ filename = booking_id
312
+
313
+ logger.info(f"Uploading file {filename} to S3", extra={'booking_id': booking_id, 'endpoint': function_name})
314
+ bucket = 'core-ai-assets'
315
+ try:
316
+ if (AWS_ACCESS_KEY and AWS_SECRET_KEY):
317
+ session = boto3.session.Session(aws_access_key_id=AWS_ACCESS_KEY, aws_secret_access_key=AWS_SECRET_KEY, region_name=REGION)
318
+ else:
319
+ session = boto3.session.Session()
320
+ s3_client = session.client('s3')
321
+ with open(path_to_upload, "rb") as f:
322
+ ## Upload to Production Folder
323
+ s3_client.upload_fileobj(f, bucket, f'staging/pre_gg_reports/{filename}.pdf')
324
+ logger.info(f"File {filename} uploaded successfully to S3", extra={'booking_id': booking_id, 'endpoint': function_name})
325
+
326
+ # Removing files
327
+ for file in os.listdir(os.path.join('bookings', 'data')):
328
+ os.remove(os.path.join('bookings', 'data', file))
329
+ for file in os.listdir(os.path.join('bookings', 'to_upload')):
330
+ os.remove(os.path.join('bookings', 'to_upload', file))
331
+
332
+ # force_file_move(os.path.join('users', 'to_upload', filename), os.path.join('users', 'data', filename))
333
+ except (FileNotFoundError, NoCredentialsError, PartialCredentialsError) as e:
334
+ logger.error(f"S3 upload failed for {filename}: {e}", extra={'booking_id': booking_id, 'endpoint': function_name})
335
+ raise
336
 
337
  def get_user_summary(user_id):
338
  function_name = get_user_summary.__name__
 
811
  logger.info(f"User summary generated successfully for user {user_id}", extra={'user_id': user_id, 'endpoint': function_name})
812
  return reports
813
 
814
+ def create_pre_gg_report(booking_id):
815
+ function_name = create_pre_gg_report.__name__
816
+
817
+ # Get user_id from booking_id
818
+ try:
819
+ logger.info(f"Retrieving booking details for {booking_id}", extra={'booking_id': booking_id, 'endpoint': function_name})
820
+ db_params = {
821
+ 'dbname': 'ourcoach',
822
+ 'user': 'ourcoach',
823
+ 'password': 'hvcTL3kN3pOG5KteT17T',
824
+ 'host': 'staging-ourcoach.cx8se8o0iaiy.ap-southeast-1.rds.amazonaws.com',
825
+ 'port': '5432'
826
+ }
827
+ try:
828
+ with psycopg2.connect(**db_params) as conn:
829
+ with conn.cursor() as cursor:
830
+ query = sql.SQL("""
831
+ select user_id
832
+ from {table}
833
+ where id = %s
834
+ """
835
+ ).format(table=sql.Identifier('public', 'booking'))
836
+ cursor.execute(query, (booking_id,))
837
+ row = cursor.fetchone()
838
+ if (row):
839
+ colnames = [desc[0] for desc in cursor.description]
840
+ booking_data = dict(zip(colnames, row))
841
+ ### MODIFY THE FORMAT OF USER DATA
842
+ user_id = booking_data['user_id']
843
+ logger.info(f"User info retrieved successfully for {user_id}", extra={'user_id': user_id, 'endpoint': function_name})
844
+ else:
845
+ logger.warning(f"No user info found for {user_id}", extra={'user_id': user_id, 'endpoint': function_name})
846
+ except psycopg2.Error as e:
847
+ logger.error(f"Database error while retrieving user info for {user_id}: {e}", extra={'user_id': user_id, 'endpoint': function_name})
848
+ raise
849
+
850
+ # Run get_user_summary
851
+ user_report = get_user_summary(user_id)
852
+
853
+ # Run generate_html
854
+ generate_html(user_report, booking_id=booking_id)
855
+
856
+ return True
857
+ except Exception as e:
858
+ logger.error(f"An error occured: {e}", extra={'booking_id': booking_id, 'endpoint': function_name})
859
+ raise
860
+
861
  def get_user_life_status(user_id):
862
  function_name = get_user_life_status.__name__
863
  logger.info(f"Generating user life status for user {user_id}", extra={'user_id': user_id, 'endpoint': function_name})
 
876
  [f"{message['role'].capitalize()}: {message['content']}" for message in user_messages]
877
  )
878
 
879
+ logger.info(f"Fetched user data for: {user_id}", extra={'user_id': user_id, 'endpoint': function_name})
880
  # Build the system prompt according to the provided instructions
881
  system_prompt = """
882
  You are an AI assistant that generates a personalized life status report for users based on their profile and chat history. Your task is to analyze the provided user data and produce a JSON output following the specified schema.
883
 
884
  **Instructions:**
885
 
886
+ 1. **Mantra of the Week:**
 
 
 
 
 
 
 
 
 
 
887
  - Create a very short encouragement quote that encapsulates the user's journey toward achieving their goals.
888
  - The mantra **MUST** be a single sentence with fewer than 5 words.
889
 
 
 
 
 
 
 
 
 
 
 
 
 
890
  **Output Format:**
891
 
892
  Produce your response in JSON format adhering to the following schema:
893
 
894
  ```json
895
  {
896
+ "mantra_of_the_week": str
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
897
  }
898
  ```
899
 
900
  **Guidelines:**
901
 
 
902
  - The `mantra_of_the_week` should be personalized, positive, and encouraging. It **MUST** be a single sentence with fewer than 5 words.
 
 
903
  """
904
 
905
  # Combine user information and chat history for context
 
946
  "schema": {
947
  "type": "object",
948
  "properties": {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
949
  "mantra_of_the_week": {
950
  "type": "string",
951
  "description": "A very short encouragement quote that encapsulates the user's journey to achieve their goals."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
952
  }
953
  },
954
  "required": [
955
+ "mantra_of_the_week"
 
 
 
956
  ],
957
  "additionalProperties": False
958
  }
 
967
  )
968
 
969
  # Get response and convert into dictionary
970
+ mantra = json.loads(response.choices[0].message.content)["mantra_of_the_week"]
971
 
972
  except Exception as e:
973
  logger.error(f"OpenAI API call failed: {e}", extra={'user_id': user_id, 'endpoint': function_name})
974
  raise e
975
 
976
+ # Get current life score
977
+ life_score = {
978
+ "overall": user.personal_growth_score + user.career_growth_score + user.relationship_score + user.mental_well_being_score + user.health_and_wellness_score,
979
+ "personal_growth": user.personal_growth_score,
980
+ "health_and_wellness": user.health_and_wellness_score,
981
+ "mental_well_being": user.mental_well_being_score,
982
+ "career_growth": user.career_growth_score,
983
+ "relationship": user.relationship_score
984
+ }
985
+ # Get current goal
986
+ current_goal = user.goal if not user.goal else user.goal[-1].content
987
+ # Get life score achievements in list
988
+ recent_wins = user.recent_wins
989
+ # Combine everything
990
+
991
+ reports = {
992
+ "life_score": life_score,
993
+ "mantra_of_the_week": mantra,
994
+ "goal": current_goal,
995
+ "recent_wins": recent_wins
996
+ }
997
+
998
  # Step 4: Return the JSON reports
999
  logger.info(f"User life status generated successfully for user {user_id}", extra={'user_id': user_id, 'endpoint': function_name})
1000
  return reports