diff --git a/backend/app/http_routes.py b/backend/app/http_routes.py index 1f6114bd..6afb78ac 100644 --- a/backend/app/http_routes.py +++ b/backend/app/http_routes.py @@ -3,7 +3,6 @@ from routes.company import Company from routes.session import Session from routes.error import Error -from routes.calendar_suggestion import CalendarSuggestion, CalendarSuggestionOldWelcomeMessage from routes.terms_and_conditions import TermsAndConditions from routes.onboarding import Onboarding from routes.voice import Voice @@ -56,8 +55,6 @@ def __init__(self, api): api.add_resource(User, '/user') api.add_resource(Company, '/company') api.add_resource(Error, '/error') - api.add_resource(CalendarSuggestion, '/calendar_suggestion') - api.add_resource(CalendarSuggestionOldWelcomeMessage, '/welcome_message') api.add_resource(TermsAndConditions, '/terms_and_conditions') api.add_resource(Onboarding, '/onboarding') api.add_resource(Voice, '/voice') diff --git a/backend/app/instructions/calendar_waiting_message.mpt b/backend/app/instructions/calendar_waiting_message.mpt deleted file mode 100644 index 4d0ee78c..00000000 --- a/backend/app/instructions/calendar_waiting_message.mpt +++ /dev/null @@ -1,26 +0,0 @@ -#! gpt-4o -#! gpt4-turbo/2023-03-15-preview -#! gpt-4-turbo-preview -#! mistral-large-latest - -YOUR CONTEXT -{{mojo_knowledge}} - -USER NAME -{{username}} - -You are currently analyzing user's planning to propose a way to help them. -Write a really short message to the user to let them know you are currently looking for a way to help them. - -Also write a message for informing the user your done when you'll be. This message will be displayed in a mobile app notification. If the user clicks on it they will see what you've found. - -Answer in following json format: -{ - "waiting_message": "", - "done_message": "" -} - -Be funny. -10 words max for each message. Use an emoji. -Speak language {{language}}. -No talk, just message. \ No newline at end of file diff --git a/backend/app/instructions/generate_suggestion_from_calendar.mpt b/backend/app/instructions/generate_suggestion_from_calendar.mpt deleted file mode 100644 index 8188f9d3..00000000 --- a/backend/app/instructions/generate_suggestion_from_calendar.mpt +++ /dev/null @@ -1,58 +0,0 @@ -#! gpt-4o -#! gpt4-turbo/2023-03-15-preview -#! gpt-4-turbo-preview - -YOUR CONTEXT -{{mojo_knowledge}} - -GLOBAL CONTEXT -{{user_datetime_context}} - -USER NAME -{{username}} - -{%if user_company_knowledge%}USER'S COMPANY KNOWLEDGE -{{user_company_knowledge}}{%endif%} - -{%if user_business_goal%}USER'S BUSINESS GOAL -{{user_business_goal}}{%endif%} - -TASKS YOU CAN ASSIST THE USER WITH: -{{user_tasks | tojson(indent=4) }} - -USER'S TODAY'S PLANNING -{{user_planning | tojson(indent=4) }} - -USER'S TASKS DONE TODAY -{{user_tasks_done_today | tojson(indent=4) }} - -Pick 1 event of the USER'S PLANNING that you could help them with using a TASK. -Depending on the event date (before or after now) and the task (for preparing or following-up on the event), the user could use your help now or later. - -The user has already done some tasks today. -Ensure you propose to the user something that is not in USER'S DONE TASKS. -USER'S DONE TASKS -{%for task in user_done_tasks%} - {{task.task}}: {{task.title}} - done at {{task.date}} -{%endfor%} -Answer in following json format: -{ - "event_id": "", - "task_pk": , - "in_user_today_tasks": , - "can_be_done_now": , - "message_for_user": "< - Engage the conversation with the user by highlighting this event and the task you could help them with. - if to_be_used=before, message to propose your help, - if to_be_used=after, invite them to come back to you when it's time to tackle the task. - Be nice and fun. Use at least 1 emoji. - Keep it short. - No greetings, no signature. - Use language {{language}}. - >", - "message_title": "", - "message_emoji": "<Emoji that will precede title. No surrogates.>", -} -Be careful not to use any special encoding in the json. Use the unicode character directly. No surrogates. -If you have nothing to propose to the user, return empty json: {} - -No talk, only json. \ No newline at end of file diff --git a/backend/app/placeholder_generator.py b/backend/app/placeholder_generator.py index ac3cd973..742d3267 100644 --- a/backend/app/placeholder_generator.py +++ b/backend/app/placeholder_generator.py @@ -12,9 +12,6 @@ class PlaceholderGenerator: "of the great explorer of the truth, the master-builder of human happiness. No one rejects, dislikes, or avoids pleasure itself, " \ "because it is pleasure, but because those who do not know how to pursue pleasure rationally encounter consequences that are extremely painful. " - calendar_suggestion_placeholder = "This is a welcome message placeholder." - calendar_suggestion_title_placeholder = "Title placeholder" - calendar_suggestion_emoji_placeholder = "👋" waiting_message_placeholder = "Waiting..." done_message_placeholder = "Done!" diff --git a/backend/app/routes/calendar_suggestion.py b/backend/app/routes/calendar_suggestion.py deleted file mode 100644 index 8bd60686..00000000 --- a/backend/app/routes/calendar_suggestion.py +++ /dev/null @@ -1,376 +0,0 @@ -import time -from flask import request -from flask_restful import Resource -from app import authenticate, db, server_socket -from mojodex_core.db import with_db_session -from mojodex_core.entities.user import User -from mojodex_core.knowledge_manager import KnowledgeManager -from mojodex_core.logging_handler import log_error -from mojodex_core.entities.db_base_entities import * -from mojodex_core.llm_engine.mpt import MPT -from placeholder_generator import PlaceholderGenerator -from mojodex_core.json_loader import json_decode_retry -from mojodex_core.logging_handler import on_json_error -from packaging import version -from datetime import datetime - -class CalendarSuggestion(Resource): - - calendar_suggestion_generator_from_calendar_mpt_filename = "instructions/generate_suggestion_from_calendar.mpt" - - calendar_suggestion_waiting_mpt_filename = "instructions/calendar_waiting_message.mpt" - - def __init__(self): - CalendarSuggestion.method_decorators = [authenticate()] - - @with_db_session - def __generate_calendar_suggestion(self, user_id, calendar_suggestion_pk, use_placeholder, planning, app_version, db_session): - try: - user = db_session.query(User).filter( - User.user_id == user_id).first() - - def get_user_tasks(user_id): - user_tasks = db_session.query(MdTask) \ - .join(MdUserTask, MdUserTask.task_fk == MdTask.task_pk) \ - .filter(MdUserTask.user_id == user_id).all() - return [{ - "icon": task.icon, - "name_for_system": task.name_for_system, - "definition": task.definition_for_system, - "task_pk": task.task_pk - } for task in user_tasks] - - def get_user_tasks_done_today(user_id): - start_date = datetime.now().replace(hour=0, minute=0, second=0, microsecond=0) - user_tasks_done_today = db_session.query(MdTask.name_for_system, MdUserTaskExecution.title, - MdUserTaskExecution.start_date) \ - .join(MdUserTask, MdUserTask.task_fk == MdTask.task_pk) \ - .join(MdUserTaskExecution, MdUserTaskExecution.user_task_fk == MdUserTask.user_task_pk) \ - .filter(MdUserTask.user_id == user_id) \ - .filter(MdUserTaskExecution.start_date >= start_date) \ - .all() - return [{ - "task": task, - "title": title, - "date": start_date.strftime("%Y-%m-%d-%Hh:%Mm"), - } for task, title, start_date in user_tasks_done_today] - - if use_placeholder: - # await 10 seconds to simulate openai - time.sleep(10) - data = {"message_for_user": PlaceholderGenerator.calendar_suggestion_placeholder, - "message_title": PlaceholderGenerator.calendar_suggestion_title_placeholder, - "message_emoji": PlaceholderGenerator.calendar_suggestion_emoji_placeholder} - else: - @json_decode_retry(retries=3, required_keys=[], on_json_error=on_json_error) - def generate(planning): - # Answer using openai - generate_suggestion_mpt = MPT(CalendarSuggestion.calendar_suggestion_generator_from_calendar_mpt_filename, mojo_knowledge=KnowledgeManager().mojodex_knowledge, - user_datetime_context=user.datetime_context, - username=user.name, - user_company_knowledge=user.company_description, - user_business_goal=user.goal, - language=user.language_code, - user_tasks=get_user_tasks(user_id), - user_planning=planning, - user_tasks_done_today=get_user_tasks_done_today( - user_id) - ) - - response = generate_suggestion_mpt.run(user_id=user_id, - temperature=1, - max_tokens=1000, - json_format=True) - return response - - data = generate(planning) - - calendar_suggestion = db_session.query(MdCalendarSuggestion) \ - .filter(MdCalendarSuggestion.calendar_suggestion_pk == calendar_suggestion_pk) \ - .filter(MdCalendarSuggestion.user_id == user_id) \ - .first() - - calendar_suggestion.text_generated_date = datetime.now() - task_pk_to_display = None - if data and not ("in_user_today_tasks" in data and data["in_user_today_tasks"]): - calendar_suggestion.event_id = data["event_id"] if "event_id" in data else None - calendar_suggestion.suggestion_text = data["message_for_user"].strip( - ) if "message_for_user" in data else None - calendar_suggestion.calendar_suggestion_title = data["message_title"].strip( - ) if "message_title" in data else None - calendar_suggestion.calendar_suggestion_emoji = data["message_emoji"].strip( - ) if "message_emoji" in data else None - calendar_suggestion.proposed_task_fk = data["task_pk"] if "task_pk" in data else None - if "event_id" in data: - # find the end date of the event - event = next( - filter(lambda x: x["eventId"] == data["event_id"], planning)) - calendar_suggestion.reminder_date = datetime.strptime(event["eventEndDate"], - "%Y-%m-%dT%H:%M:%S.%f%z") if "eventEndDate" in event else None - db_session.flush() - - # if there is an event and event is not started yet, do not propose any task - event_start_date = datetime.strptime( - event["eventStartDate"], "%Y-%m-%dT%H:%M:%S.%f%z") if "eventStartDate" in event else None - if event_start_date and event_start_date > datetime.now(event_start_date.tzinfo): - task_pk_to_display = None # Do not display any task if event is not started yet - else: - task_pk_to_display = calendar_suggestion.proposed_task_fk - - message = { - "calendar_suggestion_pk": calendar_suggestion.calendar_suggestion_pk, - "message_text": calendar_suggestion.suggestion_text, - "message_title": calendar_suggestion.calendar_suggestion_title, - "message_emoji": calendar_suggestion.calendar_suggestion_emoji, - "task_pk": task_pk_to_display - } - server_socket.emit('calendar_suggestion', message, - to=f"mojo_events_{user_id}") - - db_session.commit() - except Exception as e: - server_socket.emit('calendar_suggestion', { - "error": "error"}, to=f"mojo_events_{user_id}") - - db_session.rollback() - log_error(f"generate_calendar_suggestion: {e}", notify_admin=True) - - def __get_waiting_message(self, user_id, user_name, user_language_code, use_placeholders=False): - try: - # generate waiting message - if use_placeholders: - return {"waiting_message": PlaceholderGenerator.waiting_message_placeholder, - "done_message": PlaceholderGenerator.done_message_placeholder} - else: - return self.__generate_waiting_message(user_id, user_name, user_language_code) - except Exception as e: - raise Exception(f"__get_waiting_message: {e}") - - @json_decode_retry(retries=3, required_keys=["waiting_message", "done_message"], - on_json_error=on_json_error) - def __generate_waiting_message(self,user_id, user_name, user_language_code): - try: - - - waiting_message_mpt = MPT(CalendarSuggestion.calendar_suggestion_waiting_mpt_filename, - mojo_knowledge=KnowledgeManager().mojodex_knowledge, - # no global context so that it can be used any day / time - username=user_name, - language=user_language_code - ) - - response = waiting_message_mpt.run(user_id=user_id, temperature=1, - max_tokens=1000) - return response - except Exception as e: - raise Exception(f"get_waiting_message: {e}") - - @with_db_session - def __prepare_next_calendar_suggestion(self, user_id, db_session): - try: - calendar_suggestion = MdCalendarSuggestion( - user_id=user_id - ) - db_session.add(calendar_suggestion) - db_session.flush() - user: User = db_session.query(User).get(user_id) - waiting_json = self.__get_waiting_message( - user_id, user.name, user.language_code, use_placeholders=False) - calendar_suggestion.waiting_message = waiting_json["waiting_message"].strip( - ) if "waiting_message" in waiting_json else None - calendar_suggestion.ready_message = waiting_json["done_message"].strip( - ) if "done_message" in waiting_json else None - db_session.commit() - return calendar_suggestion - except Exception as e: - db_session.rollback() - log_error( - f"prepare_next_calendar_suggestion: {e}", notify_admin=True) - - - - # route to put a new calendar suggestion in backend. Returns waiting message. - - def put(self, user_id): - error_message = "Error getting calendar suggestion" - - try: - timestamp = request.json["datetime"] - user_planning = request.json["user_planning"] - app_version = version.parse( - request.json["version"]) if "version" in request.json else version.parse("0.0.0") - except KeyError: - return {"error": "Missing timezone in args"}, 400 - - try: - # ensure user_planning is a list of dict - if not isinstance(user_planning, list): - return {"error": "user_planning must be a list"}, 400 - for event in user_planning: - if not isinstance(event, dict): - return {"error": "user_planning must be a list of dict"}, 400 - use_placeholders = 'use_placeholder' in request.json and request.json[ - 'use_placeholder'] - - # remove from user_planning events that have been managed in past calendar suggestions - event_managed_today = db.session.query(MdCalendarSuggestion.event_id) \ - .filter(MdCalendarSuggestion.user_id == user_id) \ - .filter(MdCalendarSuggestion.creation_date >= datetime.now().replace(hour=0, minute=0, - second=0, microsecond=0)) \ - .all() - event_managed_today = [ - event_id for event_id, in event_managed_today] - planning = [ - event for event in user_planning if event["eventId"] not in event_managed_today] - if len(planning) == 0: - return {}, 200 - - # find pre-set calendar suggestion for this user or create new one - if not use_placeholders: # if placeholder, create one - # try to find a calendar suggestion pre-set and not used - calendar_suggestion = db.session.query(MdCalendarSuggestion) \ - .filter(MdCalendarSuggestion.user_id == user_id) \ - .filter(MdCalendarSuggestion.waiting_message_sent.is_(None)) \ - .filter(MdCalendarSuggestion.waiting_message.isnot(None)) \ - .first() - if calendar_suggestion: - return calendar_suggestion - - calendar_suggestion = MdCalendarSuggestion( - user_id=user_id - ) - db.session.add(calendar_suggestion) - db.session.flush() - - server_socket.start_background_task(self.__generate_calendar_suggestion, user_id, - calendar_suggestion.calendar_suggestion_pk, use_placeholders, planning, app_version) - - try: - if not calendar_suggestion.waiting_message: - user : User = db.session.query(User).get(user_id) - waiting_json = self.__get_waiting_message( - user_id, user.name, user.language_code, use_placeholders=use_placeholders) - calendar_suggestion.waiting_message = waiting_json[ - "waiting_message"].strip() if "waiting_message" in waiting_json else None - calendar_suggestion.ready_message = waiting_json[ - "done_message"].strip() if "done_message" in waiting_json else None - - except Exception as e: - log_error(f"{error_message} : {e}", notify_admin=True) - calendar_suggestion.waiting_message = "Searching how I can help you..." - calendar_suggestion.ready_message = "Look what I've found!" - - calendar_suggestion.waiting_message_sent = datetime.now() - db.session.flush() - try: - # if no preset calendar_suggestion - calendar_suggestion = db.session.query(MdCalendarSuggestion) \ - .filter(MdCalendarSuggestion.user_id == user_id) \ - .filter(MdCalendarSuggestion.waiting_message_sent.is_(None)) \ - .filter(MdCalendarSuggestion.waiting_message.isnot(None)) \ - .first() - if calendar_suggestion is None: - server_socket.start_background_task( - self.__prepare_next_calendar_suggestion, user_id) - except Exception as e: - log_error(f"{error_message} : {e}", notify_admin=True) - - db.session.commit() - response = { - "calendar_suggestion_pk": calendar_suggestion.calendar_suggestion_pk, - "waiting_message": calendar_suggestion.waiting_message, - "ready_message": calendar_suggestion.ready_message, - } - # Normally, flask_socketio will close db.session automatically after the request is done - # (https://flask.palletsprojects.com/en/2.3.x/patterns/sqlalchemy/) "Flask will automatically remove database sessions at the end of the request or when the application shuts down." - # But if may not the case because of the background task launched in this route, errors like `QueuePool limit of size 5 overflow 10 reached` may happen in the backend logs and cause issues. - # That's why here we explicitely call `db.session.close()` to close the session manually. - db.session.close() - - return response, 200 - except Exception as e: - db.session.rollback() - log_error(f"{error_message} : {e}") - db.session.close() - return {f"{error_message} : {e}"}, 400 - - # route to get a calendar suggestion from backend. Returns calendar suggestion. - def get(self, user_id): - error_message = "Error getting calendar suggestion text" - - try: - timestamp = request.args["datetime"] - calendar_suggestion_pk = request.args["calendar_suggestion_pk"] - app_version = version.parse( - request.args["version"]) if "version" in request.args else version.parse("0.0.0") - except KeyError as e: - return {"error": f"Missing field: {e}"}, 400 - - try: - # Check calendar suggestion exists - calendar_suggestion = db.session.query(MdCalendarSuggestion) \ - .filter(MdCalendarSuggestion.calendar_suggestion_pk == calendar_suggestion_pk) \ - .filter(MdCalendarSuggestion.user_id == user_id) \ - .first() - if calendar_suggestion is None: - return {"error": f"Calendar suggestion not found for this user"}, 404 - - # has calendar suggestion been generated ? If yes, return it, else return "processing" - if calendar_suggestion.text_generated_date is not None: - if calendar_suggestion.suggestion_text is None: - return {}, 200 - - return { - "calendar_suggestion_pk": calendar_suggestion.calendar_suggestion_pk, - "suggestion_text": calendar_suggestion.suggestion_text, - "message_title": calendar_suggestion.calendar_suggestion_title, - "message_emoji": calendar_suggestion.calendar_suggestion_emoji, - "task_pk": calendar_suggestion.proposed_task_fk - }, 200 - - else: - return {"status": "processing"}, 200 - except Exception as e: - log_error(f"{error_message} : {e}") - return {f"{error_message} : {e}"}, 400 - - # route to answer calendar suggestion - def post(self, user_id): - error_message = "Error treating answer to calendar suggestion" - - try: - timestamp = request.json["datetime"] - calendar_suggestion_pk = request.json["calendar_suggestion_pk"] - except KeyError as e: - return {"error": f"Missing field {e}"}, 400 - - # Logic - try: - # Check calendar suggestion exists - calendar_suggestion = db.session.query(MdCalendarSuggestion) \ - .filter(MdCalendarSuggestion.calendar_suggestion_pk == calendar_suggestion_pk) \ - .filter(MdCalendarSuggestion.user_id == user_id) \ - .first() - if calendar_suggestion is None: - return {"error": f"Calendar suggestion not found for this user"}, 404 - - if "user_reacted" in request.json and request.json["user_reacted"]: - # update calendar_suggestion - calendar_suggestion.reminder = True - elif "user_task_execution_pk" in request.json: - calendar_suggestion.triggered_user_task_execution_pk = request.json[ - "user_task_execution_pk"] - - calendar_suggestion.user_reaction_date = datetime.now() - db.session.commit() - return {"success": "ok"}, 200 - except Exception as e: - db.session.rollback() - log_error(f"{error_message} : {e}") - return {f"{error_message} : {e}"}, 500 - - -# This class is to manage old app versions calling /welcome_message -class CalendarSuggestionOldWelcomeMessage(CalendarSuggestion): - def __init__(self): - super().__init__() diff --git a/background/README.md b/background/README.md index 82289bd6..323a2ff1 100644 --- a/background/README.md +++ b/background/README.md @@ -123,8 +123,7 @@ Anyway, the process of updating a document requires embedding the new version, w - Cortex: `background/app/models/events/event_generator.py` (abstract class, implementation depends on parameters of request) This process is called any time the Backend wants to send a notification to the user whether it is a mail, push notification... The Background is only responsible for notification content generation. -For now, 2 types of events are generated: -- calendar_suggestions: will be removed soon +For now, 1 type of events are generated: - todo_daily_emails: An email to remind the user of his To-Do items for the day. Launched from the Scheduler every day at 8am, the Backend selects the user that opted-in this option and triggers the Background to generate the email content. Once the content of the notification is generated, it is sent back to the Backend which is responsible to handle it by logging it in DB and sending it with the correct service to the user. diff --git a/background/app/instructions/calendar_suggestion_reminder_notification.mpt b/background/app/instructions/calendar_suggestion_reminder_notification.mpt deleted file mode 100644 index 06af639e..00000000 --- a/background/app/instructions/calendar_suggestion_reminder_notification.mpt +++ /dev/null @@ -1,40 +0,0 @@ -#! gpt-4o -#! gpt4-turbo/2023-03-15-preview -#! gpt-4-turbo-preview -#! mistral-large-latest - - -YOUR CONTEXT -{{mojo_knowledge}} - -GLOBAL CONTEXT -{{user_datetime_context}} - -USER NAME -{{username}} - -{%if user_company_knowledge%}USER'S COMPANY KNOWLEDGE -{{user_company_knowledge}}{%endif%} - -{%if user_business_goal%}USER'S BUSINESS GOAL -{{user_business_goal}}{%endif%} - -Earlier today, you analyzed the user's calendar and proposed them the following: ---- -{{calendar_suggestion}} ---- - -The user approved your suggestion and asked you to remind them to tackle this. -Now it's time to send them the reminder - -Prepare a friendly and engaging reminder app notification message to encourage the user to take this action. -Notification is composed of a title and a message. -Title should remind the user of the context of related event and task. -Message should be short and to the point and should encourage the user to come and tackle the task. -Write notification in the following json format: -{ -"title": "<title less than 10 words>", -"message": "<message less than 20 words>" -} -Use language {{language}}. You can use 1 emoji. -No talk, just message. \ No newline at end of file diff --git a/background/app/models/events/calendar_suggestion_notifications_generator.py b/background/app/models/events/calendar_suggestion_notifications_generator.py deleted file mode 100644 index 7f672c82..00000000 --- a/background/app/models/events/calendar_suggestion_notifications_generator.py +++ /dev/null @@ -1,80 +0,0 @@ -from mojodex_core.db import with_db_session -from mojodex_core.entities.db_base_entities import MdCalendarSuggestion, MdTask -from mojodex_core.entities.user import User -from mojodex_core.json_loader import json_decode_retry - -from mojodex_core.email_sender.email_service import EmailService -from mojodex_core.knowledge_manager import KnowledgeManager -from mojodex_core.logging_handler import on_json_error -from models.events.events_generator import PushNotificationEventGenerator -from mojodex_core.llm_engine.mpt import MPT - - -class CalendarSuggestionNotificationsGenerator(PushNotificationEventGenerator): - calendar_suggestion_notification_text_mpt_filename = "instructions/calendar_suggestion_reminder_notification.mpt" - - def __init__(self, user_id, since_date, until_date): - self.user_id = user_id - self.since_date = since_date - self.until_date = until_date - super().__init__() - - @with_db_session - def _collect_data(self, db_session): - try: - user: User = db_session.query(User).get(self.user_id) - calendar_suggestion, task = db_session.query(MdCalendarSuggestion, MdTask) \ - .join(MdTask, MdTask.task_pk == MdCalendarSuggestion.proposed_task_fk) \ - .filter(MdCalendarSuggestion.reminder_date.isnot(None)) \ - .filter(MdCalendarSuggestion.reminder == True) \ - .filter(MdCalendarSuggestion.reminder_date.between(self.since_date, self.until_date)) \ - .filter(MdCalendarSuggestion.user_id == self.user_id) \ - .first() - - return user.datetime_context, user.name, user.company_description, user.goal, calendar_suggestion.suggestion_text, task.name_for_system, task.task_pk - except Exception as e: - raise Exception(f"_collect_data: {e}") - - def generate_events(self): - try: - collected_data = self._collect_data() - task_pk = collected_data[-1] - notification_message = self._generate_notif_text(*collected_data[:-1]) - - notification_title, notification_body = notification_message["title"], notification_message["message"] - data = {"user_id": self.user_id, - "task_pk": str(task_pk), - "type": "calendar_suggestion"} - self.send_event(self.user_id, - event_type="calendar_suggestion_notification", - notification_title=notification_title, - notification_body=notification_body, - data=data) - except Exception as e: - EmailService().send_technical_error_email( - f"{self.__class__.__name__} : generate_events: Error preparing notifications: {e}") - - @json_decode_retry(retries=3, required_keys=["title", "message"], on_json_error=on_json_error) - def _generate_notif_text(self, user_datetime_context, username, user_company_knowledge, - user_business_goal, calendar_suggestion, task_name): - try: - calendar_suggestion_notification = MPT(CalendarSuggestionNotificationsGenerator.calendar_suggestion_notification_text_mpt_filename, - mojo_knowledge=KnowledgeManager().mojodex_knowledge, - user_datetime_context=user_datetime_context, - username=username, - user_company_knowledge=user_company_knowledge, - user_business_goal=user_business_goal, - calendar_suggestion=calendar_suggestion - ) - - notification_message = calendar_suggestion_notification.run(user_id=self.user_id, - temperature=1, - max_tokens=50, - json_format=True, - user_task_execution_pk=None, - task_name_for_system=task_name - ) - # try to load as json to extract title and body - return notification_message - except Exception as e: - raise Exception(f"_generate_notif_text: {e}") diff --git a/background/app/routes/event_generation.py b/background/app/routes/event_generation.py index 501f2d09..1c08dc39 100644 --- a/background/app/routes/event_generation.py +++ b/background/app/routes/event_generation.py @@ -5,9 +5,7 @@ from flask_restful import Resource from app import executor, db from models.events.todo_daily_emails_generator import TodoDailyEmailsGenerator -from models.events.calendar_suggestion_notifications_generator import CalendarSuggestionNotificationsGenerator -from mojodex_core.entities.db_base_entities import MdCalendarSuggestion, MdUser -from datetime import datetime +from mojodex_core.entities.db_base_entities import MdUser class EventsGeneration(Resource): def post(self): @@ -36,22 +34,6 @@ def post(self): user_ids = [result[0] for result in user_ids_query_result] events_generator_class = TodoDailyEmailsGenerator - elif event_type == 'calendar_suggestion_notifications': - since_date = request.json['since_date'] - since_date = datetime.fromisoformat(since_date) - until_date = request.json['until_date'] - until_date = datetime.fromisoformat(until_date) - results = db.session.query(MdCalendarSuggestion.user_id) \ - .filter(MdCalendarSuggestion.reminder_date.isnot(None)) \ - .filter(MdCalendarSuggestion.reminder == True) \ - .filter(MdCalendarSuggestion.reminder_date.between(since_date, until_date)) \ - .order_by(MdCalendarSuggestion.calendar_suggestion_pk) \ - .offset(offset) \ - .limit(n_events) \ - .all() - user_ids = [result[0] for result in results] - events_generator_class = CalendarSuggestionNotificationsGenerator - kwargs = {"since_date": since_date, "until_date": until_date} else: raise Exception(f"Unknown event type {event_type}") diff --git a/docs/openAPI/backend_api.yaml b/docs/openAPI/backend_api.yaml index f5b325a4..df217669 100644 --- a/docs/openAPI/backend_api.yaml +++ b/docs/openAPI/backend_api.yaml @@ -73,216 +73,6 @@ paths: properties: error: type: string - /calendar_suggestion: - put: - tags: - - Application - summary: Put a new calendar suggestion in backend and return waiting message - parameters: - - in: header - name: Authorization - required: true - schema: - type: string - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - datetime - - user_planning - properties: - datetime: - type: string - format: date-time - user_planning: - type: array - items: - type: object - version: - type: string - use_placeholder: - type: boolean - responses: - '200': - description: Calendar suggestion and waiting message returned successfully - content: - application/json: - schema: - type: object - properties: - calendar_suggestion_pk: - type: integer - waiting_message: - type: string - ready_message: - type: string - '400': - description: Error in getting calendar suggestion - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Authentication error - content: - application/json: - schema: - type: object - properties: - error: - type: string - get: - tags: - - Application - summary: Get a calendar suggestion from backend - parameters: - - in: query - name: datetime - required: true - schema: - type: string - format: date-time - - in: query - name: calendar_suggestion_pk - required: true - schema: - type: integer - - name: version - in: query - required: true - schema: - type: string - - name: Authorization - in: header - required: true - schema: - type: string - responses: - '200': - description: Calendar suggestion text returned successfully - content: - application/json: - schema: - type: object - properties: - calendar_suggestion_pk: - type: integer - suggestion_text: - type: string - message_title: - type: string - message_emoji: - type: string - task_pk: - type: integer - '404': - description: Calendar suggestion not found for this user - content: - application/json: - schema: - type: object - properties: - error: - type: string - '400': - description: Error in getting calendar suggestion text - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Authentication error - content: - application/json: - schema: - type: object - properties: - error: - type: string - post: - tags: - - Application - summary: Answer calendar suggestion - parameters: - - in: header - name: Authorization - required: true - schema: - type: string - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - datetime - - calendar_suggestion_pk - properties: - datetime: - type: string - format: date-time - calendar_suggestion_pk: - type: integer - user_reacted: - type: boolean - user_task_execution_pk: - type: integer - responses: - '200': - description: Answer to calendar suggestion treated successfully - content: - application/json: - schema: - type: object - properties: - success: - type: string - '404': - description: Calendar suggestion not found for this user - content: - application/json: - schema: - type: object - properties: - error: - type: string - '400': - description: Error in treating answer to calendar suggestion - content: - application/json: - schema: - type: object - properties: - error: - type: string - '500': - description: Internal server error - content: - application/json: - schema: - type: object - properties: - error: - type: string - '403': - description: Authentication error - content: - application/json: - schema: - type: object - properties: - error: - type: string /check_expired_purchases: post: tags: diff --git a/docs/openAPI/background_api.yaml b/docs/openAPI/background_api.yaml index eab12adc..da152bcf 100644 --- a/docs/openAPI/background_api.yaml +++ b/docs/openAPI/background_api.yaml @@ -25,7 +25,6 @@ paths: type: string enum: - todo_daily_emails - - calendar_suggestion_notifications n_events: type: integer offset: diff --git a/docs/technical-architecture/background.md b/docs/technical-architecture/background.md index 20e3a572..69ca9b81 100644 --- a/docs/technical-architecture/background.md +++ b/docs/technical-architecture/background.md @@ -125,8 +125,7 @@ Anyway, the process of updating a document requires embedding the new version, w - Cortex: `background/app/models/events/event_generator.py` (abstract class, implementation depends on parameters of request) This process is called any time the Backend wants to send a notification to the user whether it is a mail, push notification... The Background is responsible for notification content generation and event sending. -For now, 2 types of events are generated: -- calendar_suggestions: will be removed soon +For now, 1 type of events are generated: - todo_daily_emails: An email to remind the user of his To-Do items for the day. Launched from the Scheduler every day at 8am, the Backend selects the user that opted-in this option and triggers the Background to generate the email content. diff --git a/docs/technical-architecture/llm-features/index.md b/docs/technical-architecture/llm-features/index.md index eecc4811..86a554b4 100644 --- a/docs/technical-architecture/llm-features/index.md +++ b/docs/technical-architecture/llm-features/index.md @@ -7,5 +7,3 @@ As an AI agent, Mojodex uses cognitive functions powered by LLMs. - [OpenAI GPT-4o](./openai.md) - [Azure Open AI Services](./azure.md) - [Mistral mistral-medium & mistral-large](./mistral.md) - - diff --git a/docs/technical-architecture/scheduler.md b/docs/technical-architecture/scheduler.md index da35362f..0db7d387 100644 --- a/docs/technical-architecture/scheduler.md +++ b/docs/technical-architecture/scheduler.md @@ -47,7 +47,6 @@ PurchasesExpirationChecker(3600) # check ended free_plan every 1 hour ExtractTodos(600) # extract todos every 10 minutes RescheduleTodos(3600) # reschedule todos every 1 hour if push_notifications: - CalendarSuggestionNotificationSender(600) # send calendar suggestion notifications every 10 minutes SendDailyNotifications(3600) # send daily notifications every 1 hour (filtered by timezone) if emails: #SendDailyEmails(3600) # send daily emails every 1 hour (filtered by timezone) diff --git a/mojodex_core/entities/db_base_entities.py b/mojodex_core/entities/db_base_entities.py index 5747f987..23ffb980 100644 --- a/mojodex_core/entities/db_base_entities.py +++ b/mojodex_core/entities/db_base_entities.py @@ -151,7 +151,6 @@ class MdTask(Base): md_task_predefined_action_association_: Mapped[List['MdTaskPredefinedActionAssociation']] = relationship('MdTaskPredefinedActionAssociation', foreign_keys='[MdTaskPredefinedActionAssociation.task_fk]', back_populates='md_task_') md_user_task: Mapped[List['MdUserTask']] = relationship('MdUserTask', back_populates='md_task') md_workflow_step: Mapped[List['MdWorkflowStep']] = relationship('MdWorkflowStep', back_populates='md_task') - md_calendar_suggestion: Mapped[List['MdCalendarSuggestion']] = relationship('MdCalendarSuggestion', back_populates='md_task') class MdTextEditActionDisplayedData(Base): @@ -225,7 +224,6 @@ class MdUser(Base): md_user_task: Mapped[List['MdUserTask']] = relationship('MdUserTask', back_populates='user') md_user_vocabulary: Mapped[List['MdUserVocabulary']] = relationship('MdUserVocabulary', back_populates='user') md_home_chat: Mapped[List['MdHomeChat']] = relationship('MdHomeChat', back_populates='user') - md_calendar_suggestion: Mapped[List['MdCalendarSuggestion']] = relationship('MdCalendarSuggestion', back_populates='user') md_produced_text: Mapped[List['MdProducedText']] = relationship('MdProducedText', back_populates='user') @@ -458,7 +456,7 @@ class MdWorkflowStep(Base): rank: Mapped[int] = mapped_column(Integer) user_validation_required: Mapped[bool] = mapped_column(Boolean, server_default=text('true')) review_chat_enabled: Mapped[bool] = mapped_column(Boolean, server_default=text('false')) - definition_for_system: Mapped[Optional[str]] = mapped_column(String) + definition_for_system: Mapped[Optional[str]] = mapped_column(String(255)) md_task: Mapped['MdTask'] = relationship('MdTask', back_populates='md_workflow_step') md_workflow_step_displayed_data: Mapped[List['MdWorkflowStepDisplayedData']] = relationship('MdWorkflowStepDisplayedData', back_populates='md_workflow_step') @@ -579,7 +577,6 @@ class MdUserTaskExecution(Base): md_purchase: Mapped['MdPurchase'] = relationship('MdPurchase', back_populates='md_user_task_execution') session: Mapped['MdSession'] = relationship('MdSession', back_populates='md_user_task_execution') md_user_task: Mapped['MdUserTask'] = relationship('MdUserTask', back_populates='md_user_task_execution') - md_calendar_suggestion: Mapped[List['MdCalendarSuggestion']] = relationship('MdCalendarSuggestion', back_populates='md_user_task_execution') md_produced_text: Mapped[List['MdProducedText']] = relationship('MdProducedText', back_populates='md_user_task_execution') md_todo: Mapped[List['MdTodo']] = relationship('MdTodo', back_populates='md_user_task_execution') md_user_workflow_step_execution: Mapped[List['MdUserWorkflowStepExecution']] = relationship('MdUserWorkflowStepExecution', back_populates='md_user_task_execution') @@ -601,37 +598,6 @@ class MdWorkflowStepDisplayedData(Base): md_workflow_step: Mapped['MdWorkflowStep'] = relationship('MdWorkflowStep', back_populates='md_workflow_step_displayed_data') -class MdCalendarSuggestion(Base): - __tablename__ = 'md_calendar_suggestion' - __table_args__ = ( - ForeignKeyConstraint(['proposed_task_fk'], ['md_task.task_pk'], name='md_welcome_message_md_task_fkey'), - ForeignKeyConstraint(['triggered_user_task_execution_pk'], ['md_user_task_execution.user_task_execution_pk'], name='md_welcome_message_triggered_user_task_execution_pk_fkey'), - ForeignKeyConstraint(['user_id'], ['md_user.user_id'], name='welcome_message_user_id_fkey'), - PrimaryKeyConstraint('calendar_suggestion_pk', name='md_welcome_message_pkey') - ) - - calendar_suggestion_pk: Mapped[int] = mapped_column(Integer, Sequence('welcome_message_pk_seq'), primary_key=True) - user_id: Mapped[str] = mapped_column(String(255)) - reminder: Mapped[bool] = mapped_column(Boolean, server_default=text('false')) - creation_date: Mapped[datetime.datetime] = mapped_column(DateTime, server_default=text('now()')) - suggestion_text: Mapped[Optional[str]] = mapped_column(Text) - user_reaction_date: Mapped[Optional[datetime.datetime]] = mapped_column(DateTime) - triggered_user_task_execution_pk: Mapped[Optional[int]] = mapped_column(Integer) - proposed_task_fk: Mapped[Optional[int]] = mapped_column(Integer) - text_generated_date: Mapped[Optional[datetime.datetime]] = mapped_column(DateTime(True)) - waiting_message: Mapped[Optional[str]] = mapped_column(String(255)) - suggestion_title: Mapped[Optional[str]] = mapped_column(String(255)) - suggestion_emoji: Mapped[Optional[str]] = mapped_column(String(255)) - event_id: Mapped[Optional[str]] = mapped_column(String(255)) - reminder_date: Mapped[Optional[datetime.datetime]] = mapped_column(DateTime(True)) - waiting_message_sent: Mapped[Optional[datetime.datetime]] = mapped_column(DateTime(True)) - ready_message: Mapped[Optional[str]] = mapped_column(String(255)) - - md_task: Mapped['MdTask'] = relationship('MdTask', back_populates='md_calendar_suggestion') - md_user_task_execution: Mapped['MdUserTaskExecution'] = relationship('MdUserTaskExecution', back_populates='md_calendar_suggestion') - user: Mapped['MdUser'] = relationship('MdUser', back_populates='md_calendar_suggestion') - - class MdProducedText(Base): __tablename__ = 'md_produced_text' __table_args__ = ( diff --git a/pgsql/README.md b/pgsql/README.md index 4e10396c..92541c05 100644 --- a/pgsql/README.md +++ b/pgsql/README.md @@ -92,7 +92,6 @@ You should see the following tables: List of relations Schema | Name | Type | Owner --------+-------------------------------------------+-------+------------ - public | md_calendar_suggestion | table | <your_user> public | md_company | table | <your_user> public | md_device | table | <your_user> public | md_document | table | <your_user> diff --git a/pgsql/create-mojodex-db.sql b/pgsql/create-mojodex-db.sql index a847196e..553044de 100644 --- a/pgsql/create-mojodex-db.sql +++ b/pgsql/create-mojodex-db.sql @@ -128,10 +128,10 @@ CREATE SEQUENCE public.home_chat_pk_seq -- --- Name: welcome_message_pk_seq; Type: SEQUENCE; Schema: public; Owner: - +-- Name: md_company_seq; Type: SEQUENCE; Schema: public; Owner: - -- -CREATE SEQUENCE public.welcome_message_pk_seq +CREATE SEQUENCE public.md_company_seq START WITH 1 INCREMENT BY 1 NO MINVALUE @@ -143,42 +143,6 @@ SET default_tablespace = ''; SET default_table_access_method = heap; --- --- Name: md_calendar_suggestion; Type: TABLE; Schema: public; Owner: - --- - -CREATE TABLE public.md_calendar_suggestion ( - calendar_suggestion_pk integer DEFAULT nextval('public.welcome_message_pk_seq'::regclass) NOT NULL, - user_id character varying(255) NOT NULL, - suggestion_text text, - reminder boolean DEFAULT false NOT NULL, - creation_date timestamp without time zone DEFAULT now() NOT NULL, - user_reaction_date timestamp without time zone, - triggered_user_task_execution_pk integer, - proposed_task_fk integer, - text_generated_date timestamp with time zone, - waiting_message character varying(255), - suggestion_title character varying(255), - suggestion_emoji character varying(255), - event_id character varying(255), - reminder_date timestamp with time zone, - waiting_message_sent timestamp with time zone, - ready_message character varying(255) -); - - --- --- Name: md_company_seq; Type: SEQUENCE; Schema: public; Owner: - --- - -CREATE SEQUENCE public.md_company_seq - START WITH 1 - INCREMENT BY 1 - NO MINVALUE - NO MAXVALUE - CACHE 1; - - -- -- Name: md_company; Type: TABLE; Schema: public; Owner: - -- @@ -1298,14 +1262,6 @@ ALTER TABLE ONLY public.md_user_workflow_step_execution ADD CONSTRAINT md_user_workflow_step_execution_pkey PRIMARY KEY (user_workflow_step_execution_pk); --- --- Name: md_calendar_suggestion md_welcome_message_pkey; Type: CONSTRAINT; Schema: public; Owner: - --- - -ALTER TABLE ONLY public.md_calendar_suggestion - ADD CONSTRAINT md_welcome_message_pkey PRIMARY KEY (calendar_suggestion_pk); - - -- -- Name: md_workflow_step_displayed_data md_workflow_step_displayed_data_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- @@ -1658,22 +1614,6 @@ ALTER TABLE ONLY public.md_user_workflow_step_execution ADD CONSTRAINT md_user_workflow_step_execution_workflow_step_fk_fkey FOREIGN KEY (workflow_step_fk) REFERENCES public.md_workflow_step(workflow_step_pk); --- --- Name: md_calendar_suggestion md_welcome_message_md_task_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - --- - -ALTER TABLE ONLY public.md_calendar_suggestion - ADD CONSTRAINT md_welcome_message_md_task_fkey FOREIGN KEY (proposed_task_fk) REFERENCES public.md_task(task_pk); - - --- --- Name: md_calendar_suggestion md_welcome_message_triggered_user_task_execution_pk_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - --- - -ALTER TABLE ONLY public.md_calendar_suggestion - ADD CONSTRAINT md_welcome_message_triggered_user_task_execution_pk_fkey FOREIGN KEY (triggered_user_task_execution_pk) REFERENCES public.md_user_task_execution(user_task_execution_pk); - - -- -- Name: md_workflow_step_displayed_data md_workflow_step_displayed_data_workflow_step_fk_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- @@ -1818,14 +1758,6 @@ ALTER TABLE ONLY public.md_user_workflow_step_execution_result ADD CONSTRAINT user_workflow_step_execution_fkey FOREIGN KEY (user_workflow_step_execution_fk) REFERENCES public.md_user_workflow_step_execution(user_workflow_step_execution_pk); --- --- Name: md_calendar_suggestion welcome_message_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - --- - -ALTER TABLE ONLY public.md_calendar_suggestion - ADD CONSTRAINT welcome_message_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.md_user(user_id); - - -- -- PostgreSQL database dump complete -- diff --git a/pgsql/init-mojodex-data.sql b/pgsql/init-mojodex-data.sql index 5b865cc4..ee0114ed 100644 --- a/pgsql/init-mojodex-data.sql +++ b/pgsql/init-mojodex-data.sql @@ -29,13 +29,6 @@ SET row_security = off; INSERT INTO public.md_product_category VALUES (1, 'demo', '🪄', 'Explore Mojodex''s capabilities to enhance productivity and uncover innovative solutions for various tasks', true); --- --- Data for Name: md_product; Type: TABLE DATA; Schema: public; Owner: assistant_db_user --- - -INSERT INTO public.md_product VALUES (1, NULL, 'active', 1, true, 999, NULL, NULL, 'professional_digital_assistant'); - - -- -- Data for Name: md_user; Type: TABLE DATA; Schema: public; Owner: assistant_db_user -- @@ -44,122 +37,123 @@ INSERT INTO public.md_user VALUES ('14f919cf95a70935c6c70f4a89ef5fec', 'Demo Use -- --- Data for Name: md_purchase; Type: TABLE DATA; Schema: public; Owner: assistant_db_user +-- Data for Name: md_device; Type: TABLE DATA; Schema: public; Owner: assistant_db_user -- -INSERT INTO public.md_purchase VALUES (3, '14f919cf95a70935c6c70f4a89ef5fec', 1, NULL, '2024-02-06 16:04:52.902031+00', NULL, NULL, NULL, NULL, true, NULL, NULL); -- --- Data for Name: md_session; Type: TABLE DATA; Schema: public; Owner: assistant_db_user +-- Data for Name: md_document; Type: TABLE DATA; Schema: public; Owner: assistant_db_user -- -- --- Data for Name: md_text_type; Type: TABLE DATA; Schema: public; Owner: assistant_db_user +-- Data for Name: md_document_chunk; Type: TABLE DATA; Schema: public; Owner: assistant_db_user -- -INSERT INTO public.md_text_type VALUES (1, 'meeting_minutes'); -INSERT INTO public.md_text_type VALUES (2, 'email'); -INSERT INTO public.md_text_type VALUES (3, 'document'); -INSERT INTO public.md_text_type VALUES (4, 'poem'); -- --- Data for Name: md_task; Type: TABLE DATA; Schema: public; Owner: assistant_db_user +-- Data for Name: md_session; Type: TABLE DATA; Schema: public; Owner: assistant_db_user -- -INSERT INTO public.md_task VALUES (1, 'instruct', 'prepare_meeting_minutes', 'The user needs assistance to prepare a meeting minutes', 'Write a meeting minutes in the form of bullet points', '📝', 1, 'SHORT CONTEXT - DATE OF THE DAY', 'CONTENT OF THE MEETING MINUTES', false, '[{"info_name": "key_topics", "description": "Key topics discussed in the meeting"}, {"info_name": "participants", "description": "Participants of the meeting"}, {"info_name": "date_of_meeting", "description": "Date of the meeting"}, {"info_name": "followup_actions", "description": "Followup actions if any"}]', true); -INSERT INTO public.md_task VALUES (2, 'instruct', 'follow-up_email', 'The user needs assistance to prepare a follow-up email', 'Write a follow-up email', '💌', 2, 'EMAIL SUBJECT', 'CONTENT OF THE EMAIL', false, '[{"info_name": "meeting_notes", "description": "The notes taken by the user about the meeting"}, {"info_name": "call_to_action", "description": "The follow-up that the user expects from the meeting and wants to share if any"}]', true); -INSERT INTO public.md_task VALUES (3, 'instruct', 'structure_ideas_into_doc', 'The user needs assistance to turn ideas into a structured written doc', 'Write a structured document based on the provided ideas.', '💡', 3, 'IDEA SUMMARY IN 3-5 WORDS - DATE OF THE DAY', 'CONTENT OF THE STRUCTURED DOCUMENT', false, '[]', true); -INSERT INTO public.md_task VALUES (4, 'instruct', 'prepare_linkedin_post', 'The user wants to prepare a LinkedIn post', 'Write a post for LinkedIn', '📰', 3, 'CONTEXT OF THE POST', 'CONTENT OF THE LINKEDIN POST', false, '[{"info_name": "post_context", "description": "Context that makes the user want to communicate on LinkedIn"}]', true); -INSERT INTO public.md_task VALUES (5, 'instruct', 'create_one_minute_pitch', 'The user needs assistance to create a 1-minute pitch for presenting their company and product', 'Write a 1-minute pitch to briefly present the company and product; finish with a question to engage conversation.', '🎤', 3, '1 MINUTE PITCH - COMPANY NAME', 'PITCH CONTENT', false, '[{"info_name": "problem_solved", "description": "The problem the company and product are solving"}, {"info_name": "solution", "description": "How the company and product solve the problem"}, {"info_name": "unique_selling_points", "description": "What makes the company and product different from other solutions"}, {"info_name": "target_market", "description": "The target market represented by an ideal customer description"}]', true); -INSERT INTO public.md_task VALUES (6, 'instruct', 'general_assistance', 'The user needs help with a question they may ask', 'Provide a helpful and accurate answer to the user''s question', '💡', 3, 'QUESTION ASKED BY USER', 'HELPFUL RESPONSE WITH REQUESTED INFORMATION', false, '[]', true); -- --- Data for Name: md_user_task; Type: TABLE DATA; Schema: public; Owner: assistant_db_user +-- Data for Name: md_error; Type: TABLE DATA; Schema: public; Owner: assistant_db_user -- -INSERT INTO public.md_user_task VALUES (1, '14f919cf95a70935c6c70f4a89ef5fec', 1, true); -INSERT INTO public.md_user_task VALUES (2, '14f919cf95a70935c6c70f4a89ef5fec', 2, true); -INSERT INTO public.md_user_task VALUES (3, '14f919cf95a70935c6c70f4a89ef5fec', 3, true); -INSERT INTO public.md_user_task VALUES (4, '14f919cf95a70935c6c70f4a89ef5fec', 4, true); -INSERT INTO public.md_user_task VALUES (5, '14f919cf95a70935c6c70f4a89ef5fec', 5, true); -INSERT INTO public.md_user_task VALUES (6, '14f919cf95a70935c6c70f4a89ef5fec', 6, true); -- --- Data for Name: md_user_task_execution; Type: TABLE DATA; Schema: public; Owner: assistant_db_user +-- Data for Name: md_event; Type: TABLE DATA; Schema: public; Owner: assistant_db_user -- -- --- Data for Name: md_calendar_suggestion; Type: TABLE DATA; Schema: public; Owner: assistant_db_user +-- Data for Name: md_home_chat; Type: TABLE DATA; Schema: public; Owner: assistant_db_user -- -- --- Data for Name: md_device; Type: TABLE DATA; Schema: public; Owner: assistant_db_user +-- Data for Name: md_message; Type: TABLE DATA; Schema: public; Owner: assistant_db_user -- -- --- Data for Name: md_document; Type: TABLE DATA; Schema: public; Owner: assistant_db_user +-- Data for Name: md_platform; Type: TABLE DATA; Schema: public; Owner: assistant_db_user -- +INSERT INTO public.md_platform VALUES (1, 'mobile'); +INSERT INTO public.md_platform VALUES (2, 'webapp'); -- --- Data for Name: md_document_chunk; Type: TABLE DATA; Schema: public; Owner: assistant_db_user +-- Data for Name: md_text_type; Type: TABLE DATA; Schema: public; Owner: assistant_db_user -- +INSERT INTO public.md_text_type VALUES (1, 'meeting_minutes'); +INSERT INTO public.md_text_type VALUES (2, 'email'); +INSERT INTO public.md_text_type VALUES (3, 'document'); +INSERT INTO public.md_text_type VALUES (4, 'poem'); -- --- Data for Name: md_error; Type: TABLE DATA; Schema: public; Owner: assistant_db_user +-- Data for Name: md_task; Type: TABLE DATA; Schema: public; Owner: assistant_db_user -- +INSERT INTO public.md_task VALUES (1, 'instruct', 'prepare_meeting_minutes', 'The user needs assistance to prepare a meeting minutes', 'Write a meeting minutes in the form of bullet points', '📝', 1, 'SHORT CONTEXT - DATE OF THE DAY', 'CONTENT OF THE MEETING MINUTES', false, '[{"info_name": "key_topics", "description": "Key topics discussed in the meeting"}, {"info_name": "participants", "description": "Participants of the meeting"}, {"info_name": "date_of_meeting", "description": "Date of the meeting"}, {"info_name": "followup_actions", "description": "Followup actions if any"}]', true); +INSERT INTO public.md_task VALUES (2, 'instruct', 'follow-up_email', 'The user needs assistance to prepare a follow-up email', 'Write a follow-up email', '💌', 2, 'EMAIL SUBJECT', 'CONTENT OF THE EMAIL', false, '[{"info_name": "meeting_notes", "description": "The notes taken by the user about the meeting"}, {"info_name": "call_to_action", "description": "The follow-up that the user expects from the meeting and wants to share if any"}]', true); +INSERT INTO public.md_task VALUES (3, 'instruct', 'structure_ideas_into_doc', 'The user needs assistance to turn ideas into a structured written doc', 'Write a structured document based on the provided ideas.', '💡', 3, 'IDEA SUMMARY IN 3-5 WORDS - DATE OF THE DAY', 'CONTENT OF THE STRUCTURED DOCUMENT', false, '[]', true); +INSERT INTO public.md_task VALUES (4, 'instruct', 'prepare_linkedin_post', 'The user wants to prepare a LinkedIn post', 'Write a post for LinkedIn', '📰', 3, 'CONTEXT OF THE POST', 'CONTENT OF THE LINKEDIN POST', false, '[{"info_name": "post_context", "description": "Context that makes the user want to communicate on LinkedIn"}]', true); +INSERT INTO public.md_task VALUES (5, 'instruct', 'create_one_minute_pitch', 'The user needs assistance to create a 1-minute pitch for presenting their company and product', 'Write a 1-minute pitch to briefly present the company and product; finish with a question to engage conversation.', '🎤', 3, '1 MINUTE PITCH - COMPANY NAME', 'PITCH CONTENT', false, '[{"info_name": "problem_solved", "description": "The problem the company and product are solving"}, {"info_name": "solution", "description": "How the company and product solve the problem"}, {"info_name": "unique_selling_points", "description": "What makes the company and product different from other solutions"}, {"info_name": "target_market", "description": "The target market represented by an ideal customer description"}]', true); +INSERT INTO public.md_task VALUES (6, 'instruct', 'general_assistance', 'The user needs help with a question they may ask', 'Provide a helpful and accurate answer to the user''s question', '💡', 3, 'QUESTION ASKED BY USER', 'HELPFUL RESPONSE WITH REQUESTED INFORMATION', false, '[]', true); -- --- Data for Name: md_event; Type: TABLE DATA; Schema: public; Owner: assistant_db_user +-- Data for Name: md_task_predefined_action_association; Type: TABLE DATA; Schema: public; Owner: assistant_db_user -- -- --- Data for Name: md_home_chat; Type: TABLE DATA; Schema: public; Owner: assistant_db_user +-- Data for Name: md_predefined_action_displayed_data; Type: TABLE DATA; Schema: public; Owner: assistant_db_user -- -- --- Data for Name: md_message; Type: TABLE DATA; Schema: public; Owner: assistant_db_user +-- Data for Name: md_product; Type: TABLE DATA; Schema: public; Owner: assistant_db_user -- +INSERT INTO public.md_product VALUES (1, NULL, 'active', 1, true, 999, NULL, NULL, 'professional_digital_assistant'); -- --- Data for Name: md_platform; Type: TABLE DATA; Schema: public; Owner: assistant_db_user +-- Data for Name: md_purchase; Type: TABLE DATA; Schema: public; Owner: assistant_db_user -- -INSERT INTO public.md_platform VALUES (1, 'mobile'); -INSERT INTO public.md_platform VALUES (2, 'webapp'); +INSERT INTO public.md_purchase VALUES (3, '14f919cf95a70935c6c70f4a89ef5fec', 1, NULL, '2024-02-06 16:04:52.902031+00', NULL, NULL, NULL, NULL, true, NULL, NULL); -- --- Data for Name: md_task_predefined_action_association; Type: TABLE DATA; Schema: public; Owner: assistant_db_user +-- Data for Name: md_user_task; Type: TABLE DATA; Schema: public; Owner: assistant_db_user -- +INSERT INTO public.md_user_task VALUES (1, '14f919cf95a70935c6c70f4a89ef5fec', 1, true); +INSERT INTO public.md_user_task VALUES (2, '14f919cf95a70935c6c70f4a89ef5fec', 2, true); +INSERT INTO public.md_user_task VALUES (3, '14f919cf95a70935c6c70f4a89ef5fec', 3, true); +INSERT INTO public.md_user_task VALUES (4, '14f919cf95a70935c6c70f4a89ef5fec', 4, true); +INSERT INTO public.md_user_task VALUES (5, '14f919cf95a70935c6c70f4a89ef5fec', 5, true); +INSERT INTO public.md_user_task VALUES (6, '14f919cf95a70935c6c70f4a89ef5fec', 6, true); -- --- Data for Name: md_predefined_action_displayed_data; Type: TABLE DATA; Schema: public; Owner: assistant_db_user +-- Data for Name: md_user_task_execution; Type: TABLE DATA; Schema: public; Owner: assistant_db_user -- @@ -547,13 +541,6 @@ SELECT pg_catalog.setval('public.md_workflow_step_displayed_data_seq', 1, false) SELECT pg_catalog.setval('public.md_workflow_step_seq', 1, false); --- --- Name: welcome_message_pk_seq; Type: SEQUENCE SET; Schema: public; Owner: assistant_db_user --- - -SELECT pg_catalog.setval('public.welcome_message_pk_seq', 1, false); - - -- -- PostgreSQL database dump complete -- diff --git a/scheduler/README.md b/scheduler/README.md index 6bae6238..b4dae588 100644 --- a/scheduler/README.md +++ b/scheduler/README.md @@ -47,7 +47,6 @@ PurchasesExpirationChecker(3600) # check ended free_plan every 1 hour ExtractTodos(600) # extract todos every 10 minutes RescheduleTodos(3600) # reschedule todos every 1 hour if push_notifications: - CalendarSuggestionNotificationSender(600) # send calendar suggestion notifications every 10 minutes SendDailyNotifications(3600) # send daily notifications every 1 hour (filtered by timezone) if emails: #SendDailyEmails(3600) # send daily emails every 1 hour (filtered by timezone) diff --git a/scheduler/app/main.py b/scheduler/app/main.py index feb61f9e..e81f3750 100644 --- a/scheduler/app/main.py +++ b/scheduler/app/main.py @@ -8,7 +8,6 @@ from scheduled_tasks.reschedule_todos import RescheduleTodos from scheduled_tasks.send_todo_daily_emails import SendTodoDailyEmails from scheduled_tasks.purchase_expiration_checker import PurchasesExpirationChecker -from scheduled_tasks.send_calendar_suggestion_notifications import CalendarSuggestionNotificationSender from scheduled_tasks.first_home_chat_of_week import FirstHomeChatOfWeek from scheduled_tasks.relaunch_locked_steps import RelaunchLockedSteps from datetime import datetime @@ -64,8 +63,13 @@ def _check_push_notifications_are_configured(): PurchasesExpirationChecker(3600) # check ended free_plan every 1 hour ExtractTodos(600) # extract todos every 10 minutes RescheduleTodos(3600) # reschedule todos every 1 hour -if _check_push_notifications_are_configured(): - CalendarSuggestionNotificationSender(600) # send calendar suggestion notifications every 10 minutes + + +## TO USE PUSH NOTIFICATIONS use this snippet as example +# if _check_push_notifications_are_configured(): +# pass + + if _check_emails_are_configured(): SendTodoDailyEmails(3600) # send todo daily emails every 1 hour (filtered by timezone) CheckDisengagedFreeTrialUsers(86400) # check disengaged free trial users every day diff --git a/scheduler/app/scheduled_tasks/send_calendar_suggestion_notifications.py b/scheduler/app/scheduled_tasks/send_calendar_suggestion_notifications.py deleted file mode 100644 index 720300fc..00000000 --- a/scheduler/app/scheduled_tasks/send_calendar_suggestion_notifications.py +++ /dev/null @@ -1,27 +0,0 @@ -import os -from datetime import datetime, timedelta, timezone -from scheduled_tasks.scheduled_task import ScheduledTask -import requests - -class CalendarSuggestionNotificationSender(ScheduledTask): - logger_prefix = "CalendarSuggestionNotificationSender" - - def job(self, offset=0, since_date=None, until_date=None, batch_size=50): - try: - uri = f"{os.environ['BACKGROUND_BACKEND_URI']}/events_generation" - now_date = datetime.now(timezone.utc) - since_date = since_date if since_date else now_date - timedelta(seconds=600) - until_date = now_date if until_date is None else until_date - pload = {'datetime': datetime.now().isoformat(), - 'n_events': batch_size, 'offset': offset, 'since_date': since_date.isoformat(), "until_date": until_date.isoformat(), 'event_type': 'calendar_suggestion_notifications'} - headers = {'Content-Type': 'application/json'} - internal_request = requests.post(uri, json=pload, headers=headers) - if internal_request.status_code != 200: - self.logger.error(f"Error sending calendar suggestion notifications : {internal_request.text}") - else: - user_ids = internal_request.json()['user_ids'] - self.logger.info(f"Calendar suggestion notifications successfully launched. Those users are concerned: {user_ids}") - if len(user_ids) == batch_size: - self.job(offset=offset+batch_size, since_date=since_date, until_date=until_date) - except Exception as e: - self.logger.error(f"Error sending calendar suggestions notifications : {e}") \ No newline at end of file