Skip to content

Commit

Permalink
Merge pull request #202 from PerfectFit-project/481-general-activity-…
Browse files Browse the repository at this point in the history
…uncompleted

General activity restart dialog
  • Loading branch information
BendikC authored Nov 21, 2023
2 parents 8310b76 + 7aafe47 commit b1b69fd
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 16 deletions.
7 changes: 7 additions & 0 deletions Rasa_Bot/actions/actions_weekly_reflection.py
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,13 @@ def name(self):

async def run(self, dispatcher, tracker, domain):
step_goal_days = tracker.get_slot('step_goal_days')
user_id = int(tracker.current_state()['sender_id'])

if step_goal_days is None:
logging.error(f'User id: {user_id}, dialog: weekly reflection,'
'action: action_step_goal_utterances')
step_goal_days = 0

if step_goal_days > 5:
dispatcher.utter_message(response="utter_overview_group1_4")
elif 3 < step_goal_days < 6:
Expand Down
22 changes: 18 additions & 4 deletions scheduler/state_machine/controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
save_fsm_state_in_db,
schedule_next_execution, store_completed_dialog,
store_scheduled_dialog, update_execution_week,
update_fsm_dialog_running_status)
update_fsm_dialog_running_status,
dialogs_to_be_completed, get_component_id)
from state_machine.const import (ACTIVITY_C2_9_DAY_TRIGGER, FUTURE_SELF_INTRO, GOAL_SETTING,
TRACKING_DURATION, TIMEZONE, PREPARATION_GA, PAUSE_AND_TRIGGER,
MAX_PREPARATION_DURATION, HIGH_PA_GROUP,
Expand Down Expand Up @@ -244,9 +245,12 @@ def on_new_day(self, current_date: date):
choose_sport_completed = get_activity_completion_state(self.user_id, 29)
if ((current_date - start_date).days >= ACTIVITY_C2_9_DAY_TRIGGER
and not choose_sport_completed):
plan_and_store(user_id=self.user_id,
dialog=Components.GENERAL_ACTIVITY,
phase_id=1)
if self.check_if_general_activity_dialog_exists():
run_uncompleted_dialog(self.user_id, dialog_preference=Components.GENERAL_ACTIVITY)
else:
plan_and_store(user_id=self.user_id,
dialog=Components.GENERAL_ACTIVITY,
phase_id=1)

self.check_if_end_date(current_date)

Expand All @@ -259,6 +263,16 @@ def check_if_end_date(self, date_to_check: date) -> bool:

return False

def check_if_general_activity_dialog_exists(self):
"""
Check if there is an uncompleted general activity dialog in the database for the given user.
"""
general_activity_id = get_component_id(Components.GENERAL_ACTIVITY)
uncompleted_dialogs = dialogs_to_be_completed(self.user_id)
dialog_ids = list(map(lambda dialog: dialog.intervention_component_id, uncompleted_dialogs))

return general_activity_id in dialog_ids


class GoalsSettingState(State):

Expand Down
87 changes: 75 additions & 12 deletions scheduler/state_machine/state_machine_utils.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from typing import Optional

import logging
from typing import Optional, List
from celery import Celery
from datetime import datetime, date, timedelta
from sqlalchemy.exc import NoResultFound
Expand Down Expand Up @@ -783,7 +783,7 @@ def update_fsm_dialog_running_status(user_id: int, dialog_running: bool):
session.close()


def dialog_to_be_completed(user_id: int) -> Optional[UserInterventionState]:
def dialogs_to_be_completed(user_id: int) -> List[UserInterventionState]:
"""
Checks if a dialog has to be completed and, in this case returns it. If no dialogs have to be
completed, it triggers the menu message without the option of resuming a dialog
Expand All @@ -806,29 +806,35 @@ def dialog_to_be_completed(user_id: int) -> Optional[UserInterventionState]:
UserInterventionState.completed.is_(False),
UserInterventionState.last_time.isnot(None)
)
.first()
.all()
)

session.expunge(uncompleted)
result = [UserInterventionState(id=task.id,
users_nicedayuid=task.users_nicedayuid,
intervention_phase_id=task.intervention_phase_id,
intervention_component_id=task.intervention_component_id,
completed=task.completed,
last_time=task.last_time,
last_part=task.last_part,
next_planned_date=task.next_planned_date,
task_uuid=task.task_uuid) for task in uncompleted]

session.close()

if uncompleted is not None:
return uncompleted

return None
return result


def run_uncompleted_dialog(user_id: int):
def run_uncompleted_dialog(user_id: int, dialog_preference: Components = None):
"""
Checks if a dialog has to be completed and, in this case runs it from the latest completed part.
Args:
user_id: ID of the user
dialog_preference: Dialog to prioritize when selecting which one to run
"""

uncompleted = dialog_to_be_completed(user_id)
uncompleted = dialogs_to_be_completed(user_id)
uncompleted = select_dialog_to_complete(uncompleted, dialog_preference)

if uncompleted is not None:
# update the time in the DB and trigger it
Expand All @@ -849,6 +855,63 @@ def run_uncompleted_dialog(user_id: int):
else:
run_option_menu(user_id)

def select_dialog_to_complete(uncompleted_dialogs: List[UserInterventionState],
dialog_preference: Components) -> Optional[UserInterventionState]:
"""
Selects the dialog from the list of uncompleted dialogs for the user to go through with.
If there is no preference, then we select a random dialog which isn't a general activity
dialog. If all else fails we take the first one.
Args:
uncompleted_dialogs: A list of dialogs that are marked as uncompleted in the database
dialog_preference: A specific dialog that takes priority for selection
"""
if uncompleted_dialogs is None or len(uncompleted_dialogs) == 0:
return None

general_activity_id = get_component_id(Components.GENERAL_ACTIVITY)

if dialog_preference is not None:
preference_id = get_component_id(dialog_preference)
for dialog in uncompleted_dialogs:
if dialog.intervention_component_id == preference_id:
return dialog
else:
for dialog in uncompleted_dialogs:
if dialog.intervention_component_id != general_activity_id:
return dialog

return uncompleted_dialogs[0]

def get_component_id(dialog: Components) -> Optional[int]:
"""
For a given dialog, retrieve its ID from the database.
Args:
dialog: The component for which to retrieve the ID
"""
session = get_db_session()

intervention_component = (
session.query(
InterventionComponents
)
.filter(
InterventionComponents.intervention_component_name == dialog.value
)
.first()
)

user_id = intervention_component.intervention_component_id
session.close()

if intervention_component is None:
logging.error(dialog.value, " not found in the database")
return None

return user_id



def run_option_menu(user_id: int):
"""
Expand Down

0 comments on commit b1b69fd

Please sign in to comment.