diff --git a/Rasa_Bot/Dockerfile b/Rasa_Bot/Dockerfile index 7f23a6ae..18acbf2c 100644 --- a/Rasa_Bot/Dockerfile +++ b/Rasa_Bot/Dockerfile @@ -2,8 +2,7 @@ FROM python:3.8 -# Change to root user to install dependencies -USER root +ENV MAX_NUMBER_OF_PREDICTIONS=30 RUN python -m pip install --upgrade pip==20.2 @@ -18,7 +17,4 @@ COPY . /app RUN chmod -R 777 /app -# Don't use root user to run code -USER 1001 - CMD ["rasa", "run", "--enable-api"] diff --git a/Rasa_Bot/actions/actions_general_activity.py b/Rasa_Bot/actions/actions_general_activity.py new file mode 100644 index 00000000..1bf011a3 --- /dev/null +++ b/Rasa_Bot/actions/actions_general_activity.py @@ -0,0 +1,498 @@ +import secrets + +from sqlalchemy import update +from virtual_coach_db.dbschema.models import (InterventionActivitiesPerformed, FirstAidKit, + InterventionActivity) +from virtual_coach_db.helper import ExecutionInterventionComponents +from virtual_coach_db.helper.helper_functions import get_db_session +from .definitions import DATABASE_URL, NUM_TOP_ACTIVITIES +from .helper import get_latest_bot_utterance +from rasa_sdk import Action, FormValidationAction, Tracker +from rasa_sdk.events import SlotSet +from rasa_sdk.executor import CollectingDispatcher +from typing import Text, Dict, Any + + +class CheckIfFirstExecutionGA(Action): + """Check if it is the first execution""" + + def name(self): + return "check_if_first_execution_ga" + + async def run(self, dispatcher, tracker, domain): + + user_id = tracker.current_state()['sender_id'] + session = get_db_session(db_url=DATABASE_URL) + + performed_activity = ( + session.query( + InterventionActivitiesPerformed + ) + .filter( + InterventionActivitiesPerformed.users_nicedayuid == user_id + ) + .all() + ) + + # if the query result is empty, no activity has been performed yet + first_execution = not bool(performed_activity) + + return [SlotSet("general_activity_first_execution", first_execution)] + + +class GeneralActivityCheckRating(Action): + """Check if the activity rating is in top five or not""" + + def name(self): + return "general_activity_check_rating" + + async def run(self, dispatcher, tracker, domain): + + rating_value = int(tracker.get_slot('activity_useful_rating')) + activity_id = int(tracker.get_slot('last_activity_id_slot')) + + session = get_db_session(db_url=DATABASE_URL) + user_id = tracker.current_state()['sender_id'] + + # get the highest scored activities + top_five_activities = ( + session.query( + FirstAidKit + ).order_by(FirstAidKit.activity_rating.desc()) + .filter( + FirstAidKit.users_nicedayuid == user_id + ) + .limit(NUM_TOP_ACTIVITIES).all() + ) + + current_record = ( + session.query( + FirstAidKit + ) + .filter( + FirstAidKit.users_nicedayuid == user_id, + FirstAidKit.intervention_activity_id == activity_id + ) + .all() + ) + + lowest_score = top_five_activities[-1].activity_rating + + # if the activity is not in the FAK, add it + if not current_record: + + save_activity_to_fak(user_id, activity_id, rating_value) + + session.commit() + + return [SlotSet("general_activity_low_high_rating", 'high')] + + # update the row containing the activity with the new rating + session.execute( + update(FirstAidKit) + .where(FirstAidKit.first_aid_kit_id == current_record[0].first_aid_kit_id) + .values(activity_rating=rating_value) + ) + + session.commit() + + if rating_value > lowest_score: + return [SlotSet("general_activity_low_high_rating", 'high')] + + return [SlotSet("general_activity_low_high_rating", 'low')] + + +class GetActivityUserInput(Action): + """Get the user input and save it in the slot""" + + def name(self): + return "get_activity_user_input" + + async def run(self, dispatcher, tracker, domain): + + activity_id = tracker.get_slot('last_activity_id_slot') + user_id = tracker.current_state()['sender_id'] + + user_inputs = get_user_intervention_activity_inputs(user_id, activity_id) + last_input = user_inputs[-1].user_input + + return [SlotSet("activity_user_input", last_input)] + + +class CheckUserInputRequired(Action): + """Check if the activity requires the input of the user""" + + def name(self): + return "check_user_input_required" + + async def run(self, dispatcher, tracker, domain): + + activity_id = tracker.get_slot('last_activity_id_slot') + session = get_db_session(db_url=DATABASE_URL) + + is_input_required = ( + session.query( + InterventionActivity + ) + .filter( + InterventionActivity.intervention_activity_id == activity_id + ).all() + ) + + return [SlotSet("is_user_input_required", is_input_required[0].user_input_required)] + + +class CheckActivityDone(Action): + """Check if an input for the activity has been + already provided by the user""" + + def name(self): + return "check_activity_done" + + async def run(self, dispatcher, tracker, domain): + + activity_id = tracker.get_slot('last_activity_id_slot') + user_id = tracker.current_state()['sender_id'] + + user_inputs = get_user_intervention_activity_inputs(user_id, activity_id) + + if user_inputs[-1].user_input is None: + activity_done = False + else: + activity_done = True + + return [SlotSet("is_activity_done", activity_done)] + + +class ValidateActivityUsefulnessForm(FormValidationAction): + def name(self) -> Text: + return 'validate_activity_usefulness_form' + + def validate_activity_useful_rating( + self, value: Text, dispatcher: CollectingDispatcher, + tracker: Tracker, domain: Dict[Text, Any]) -> Dict[Text, Any]: + # pylint: disable=unused-argument + """Validate activity_useful_rating input.""" + last_utterance = get_latest_bot_utterance(tracker.events) + + if last_utterance != 'utter_ask_activity_useful_rating': + return {"activity_useful_rating": None} + + if not self._validate_activity_useful_rating_response(value): + dispatcher.utter_message(response="utter_please_answer_0_to_10") + return {"activity_useful_rating": None} + + return {"activity_useful_rating": value} + + @staticmethod + def _validate_activity_useful_rating_response(value): + if 0 <= int(value) <= 10: + return True + return False + + +class ValidateSaveOrEditForm(FormValidationAction): + def name(self) -> Text: + return 'validate_save_or_edit_form' + + def validate_save_or_edit_slot( + self, value: Text, dispatcher: CollectingDispatcher, + tracker: Tracker, domain: Dict[Text, Any]) -> Dict[Text, Any]: + # pylint: disable=unused-argument + """Validate save_or_edit_slot input.""" + + last_utterance = get_latest_bot_utterance(tracker.events) + + if last_utterance != 'utter_ask_save_or_edit_slot': + return {"save_or_edit_slot": None} + + if not self._validate_save_or_edit_response(value): + dispatcher.utter_message(response="utter_please_answer_1_2") + return {"save_or_edit_slot": None} + + return {"save_or_edit_slot": value} + + @staticmethod + def _validate_save_or_edit_response(value): + if value in ('1', '2'): + return True + return False + + +class ValidateGeneralActivityDescriptionForm(FormValidationAction): + def name(self) -> Text: + return 'validate_general_activity_description_form' + + def validate_general_activity_description_slot( + self, value: Text, dispatcher: CollectingDispatcher, + tracker: Tracker, domain: Dict[Text, Any]) -> Dict[Text, Any]: + # pylint: disable=unused-argument + """Validate general_activity_description_slot input.""" + + last_utterance = get_latest_bot_utterance(tracker.events) + + if last_utterance != 'utter_ask_general_activity_description_slot': + return {"general_activity_description_slot": None} + + if not self._validate_response_length(value): + dispatcher.utter_message(response="utter_give_more_details") + return {"general_activity_description_slot": None} + + return {"general_activity_description_slot": value} + + @staticmethod + def _validate_response_length(value): + if len(value) >= 50: + return True + return False + + +class SaveDescriptionInDb(Action): + def name(self) -> Text: + return 'save_description_in_db' + + async def run(self, dispatcher, tracker, domain): + """Save the provided description inf the DB.""" + + description = tracker.get_slot('general_activity_description_slot') + + activity_id = tracker.get_slot('last_activity_id_slot') + user_id = tracker.current_state()['sender_id'] + + user_inputs = get_user_intervention_activity_inputs(user_id, activity_id) + row_id = user_inputs[-1].intervention_activities_performed_id + + session = get_db_session(db_url=DATABASE_URL) + + session.execute( + update(InterventionActivitiesPerformed) + .where(InterventionActivitiesPerformed.intervention_activities_performed_id == row_id) + .values( + user_input=description + ) + ) + + session.commit() + + return [] + + +class GetThreeRandomActivities(Action): + def name(self) -> Text: + return 'get_three_random_activities' + + async def run(self, dispatcher, tracker, domain): + """pick three random activities and sets the slots""" + + activity_id = tracker.get_slot('last_activity_id_slot') + + rnd_activities = get_random_activities(activity_id, 3) + rnd_activities_ids = [activity.intervention_activity_id for activity in rnd_activities] + + return [SlotSet("activity1_name", rnd_activities[0].intervention_activity_title), + SlotSet("activity2_name", rnd_activities[1].intervention_activity_title), + SlotSet("activity3_name", rnd_activities[2].intervention_activity_title), + SlotSet("rnd_activities_ids", rnd_activities_ids)] + + +class ValidateGeneralActivityNextActivityForm(FormValidationAction): + def name(self) -> Text: + return 'validate_general_activity_next_activity_form' + + def validate_general_activity_next_activity_slot( + self, value: Text, dispatcher: CollectingDispatcher, + tracker: Tracker, domain: Dict[Text, Any]) -> Dict[Text, Any]: + # pylint: disable=unused-argument + """Validate general_activity_next_activity_slot input.""" + last_utterance = get_latest_bot_utterance(tracker.events) + + if last_utterance != 'utter_ask_general_activity_next_activity_slot': + return {"general_activity_next_activity_slot": None} + + if not self._validate_response_value(value): + dispatcher.utter_message(response="utter_please_answer_1_2_3_4") + return {"general_activity_next_activity_slot": None} + + if value == '4': + activity_id = tracker.get_slot('last_activity_id_slot') + + rnd_activities = get_random_activities(activity_id, 3) + rnd_activities_ids = [activity.intervention_activity_id for activity in rnd_activities] + + return {"general_activity_next_activity_slot": None, + "activity1_name": rnd_activities[0].intervention_activity_title, + "activity2_name": rnd_activities[1].intervention_activity_title, + "activity3_name": rnd_activities[2].intervention_activity_title, + "rnd_activities_ids": rnd_activities_ids} + + return {"general_activity_next_activity_slot": value} + + @staticmethod + def _validate_response_value(value): + if 1 <= int(value) <= 4: + return True + return False + + +class GetLastPerformedActivity(Action): + """Get the last performed activity""" + + def name(self): + return "get_last_performed_activity" + + async def run(self, dispatcher, tracker, domain): + # get the last completed activity from DB and populate the slot + + session = get_db_session(db_url=DATABASE_URL) + user_id = tracker.current_state()['sender_id'] + last_activity = ( + session.query( + InterventionActivitiesPerformed + ) + .order_by(InterventionActivitiesPerformed.completed_datetime.desc()) + .filter( + InterventionActivitiesPerformed.users_nicedayuid == user_id + ).all() + ) + + if last_activity is not None: + activity_title = last_activity[0].intervention_activity.intervention_activity_title + activity_id = last_activity[0].intervention_activity.intervention_activity_id + return [SlotSet("last_activity_slot", activity_title), + SlotSet("last_activity_id_slot", activity_id)] + + return [SlotSet("last_activity_slot", None), + SlotSet("last_activity_id_slot", None)] + + +class GetActivityCoachChoice(Action): + """The coach chooses the next activity and saves it in the slot""" + + def name(self): + return "get_activity_coach_choice" + + async def run(self, dispatcher, tracker, domain): + # for testing purposes, returns a random title + # TODO: implement logic + + return [SlotSet("chosen_activity_slot", "this is the chosen activity")] + + +class CheckWhoDecides(Action): + """Check if the user or the coach decides the next activity""" + + def name(self): + return "check_who_decides" + + async def run(self, dispatcher, tracker, domain): + # for testing purposes, the user decides + # TODO: implement logic + + decider = 'user' + + return [SlotSet("who_decides_slot", decider)] + + +class LoadActivity(Action): + """load the activity instructions""" + + def name(self): + return "load_activity" + + async def run(self, dispatcher, tracker, domain): + + chosen_option = int(tracker.get_slot('general_activity_next_activity_slot')) + activities_slot = tracker.get_slot('rnd_activities_ids') + user_id = tracker.current_state()['sender_id'] + + activity_id = activities_slot[chosen_option - 1] + session = get_db_session(db_url=DATABASE_URL) + + user_inputs = get_user_intervention_activity_inputs(user_id, activity_id) + + if user_inputs: + previous_input = user_inputs[-1].user_input + else: + previous_input = None + + # save the activity to the DB + session.add( + InterventionActivitiesPerformed(users_nicedayuid=user_id, + intervention_activity_id=activity_id, + user_input=previous_input) + ) + + session.commit() + + # get the instructions + instructions = ( + session.query( + InterventionActivity + ) + .filter( + InterventionActivity.intervention_activity_id == activity_id + ).all() + ) + + # prompt the message + dispatcher.utter_message(text=instructions[0].intervention_activity_full_instructions) + return [] + + +class SetSlotGeneralActivity(Action): + def name(self): + return "action_set_slot_general_activity" + + async def run(self, dispatcher, tracker, domain): + return [SlotSet("current_intervention_component", + ExecutionInterventionComponents.GENERAL_ACTIVITY)] + + +def save_activity_to_fak(user_id: int, activity_id: int, rating_value: int): + session = get_db_session(db_url=DATABASE_URL) + + session.add( + FirstAidKit(users_nicedayuid=user_id, + intervention_activity_id=activity_id, + activity_rating=rating_value) + ) + session.commit() + + +def get_user_intervention_activity_inputs(user_id: int, activity_id: int): + session = get_db_session(db_url=DATABASE_URL) + + user_inputs = ( + session.query( + InterventionActivitiesPerformed + ) + .filter( + InterventionActivitiesPerformed.users_nicedayuid == user_id, + InterventionActivitiesPerformed.intervention_activity_id == activity_id + ).all() + ) + + return user_inputs + + +def get_random_activities(avoid_activity_id: int, number_of_activities: int): + session = get_db_session(db_url=DATABASE_URL) + + available_activities = ( + session.query( + InterventionActivity + ) + .filter( + InterventionActivity.intervention_activity_id != avoid_activity_id + ) + .all() + ) + + rnd_activities = [] + + for _ in range(number_of_activities): + random_choice = secrets.choice(available_activities) + rnd_activities.append(random_choice) + available_activities.remove(random_choice) + + return rnd_activities diff --git a/Rasa_Bot/actions/actions_rescheduling_dialog.py b/Rasa_Bot/actions/actions_rescheduling_dialog.py index 5a4dc777..36dd922e 100644 --- a/Rasa_Bot/actions/actions_rescheduling_dialog.py +++ b/Rasa_Bot/actions/actions_rescheduling_dialog.py @@ -13,6 +13,7 @@ from .definitions import REDIS_URL from .definitions import TIMEZONE from .definitions import MORNING, AFTERNOON, EVENING +from .helper import get_latest_bot_utterance celery = Celery(broker=REDIS_URL) @@ -36,6 +37,10 @@ def validate_rescheduling_now( tracker: Tracker, domain: Dict[Text, Any]) -> Dict[Text, Any]: # pylint: disable=unused-argument """Validate rescheduling_now input.""" + last_utterance = get_latest_bot_utterance(tracker.events) + + if last_utterance != 'utter_ask_rescheduling_now_or_later_form_rescheduling_now': + return {"rescheduling_now": None} now_or_later = self._validate_now_or_later_response(value) if now_or_later is None: @@ -203,3 +208,15 @@ def get_reschedule_date(timestamp: float, choice: int) -> datetime.datetime: reschedule_time = evening_time return reschedule_time + + +class ActionSetContinuation(Action): + """Set the dialog_to_continue slot""" + + def name(self): + return "action_set_continuation" + + async def run(self, dispatcher, tracker, domain): + current_intervention = tracker.get_slot('current_intervention_component') + + return [SlotSet("dialog_to_continue", current_intervention)] diff --git a/Rasa_Bot/actions/helper.py b/Rasa_Bot/actions/helper.py index ef591eca..9603a783 100644 --- a/Rasa_Bot/actions/helper.py +++ b/Rasa_Bot/actions/helper.py @@ -54,6 +54,22 @@ def get_intervention_component_id(intervention_component_name: str) -> int: intervention_component_id = selected[0].intervention_component_id return intervention_component_id + +def get_latest_bot_utterance(events): + events_bot = [] + + for event in events: + if event['event'] == 'bot': + events_bot.append(event) + + if len(events_bot) != 0: + last_utterance = events_bot[-1]['metadata']['utter_action'] + else: + last_utterance = None + + return last_utterance + + def week_day_to_numerical_form(week_day): if week_day.lower() == "monday": return 1 @@ -69,4 +85,4 @@ def week_day_to_numerical_form(week_day): return 6 if week_day.lower() == "sunday": return 7 - return -1 \ No newline at end of file + return -1 diff --git a/Rasa_Bot/data/nlu.yml b/Rasa_Bot/data/nlu.yml index 4664119b..e34bd5f2 100644 --- a/Rasa_Bot/data/nlu.yml +++ b/Rasa_Bot/data/nlu.yml @@ -1,6 +1,13 @@ version: "3.1" nlu: +- intent: review_last_activity + examples: | + - "Mijn activiteit" + - "activiteit?" + - "Activiteit" + - "Laaste activiteit" + - intent: request_plan_week examples: | - "Kan ik de agenda voor de week krijgen?" @@ -109,4 +116,4 @@ nlu: examples: | - "file" - "File" - - "Start file" + - "Start file" \ No newline at end of file diff --git a/Rasa_Bot/data/stories_general_activity.yml b/Rasa_Bot/data/stories_general_activity.yml new file mode 100644 index 00000000..6c197e77 --- /dev/null +++ b/Rasa_Bot/data/stories_general_activity.yml @@ -0,0 +1,163 @@ +## Activities dialog stories +# Purpose: Reflecting previous (=last) activity that the user has done +# and picking the next activity. +version: "3.1" + +## 1a: Trigger general activity dialog + +stories: +- story: start dialog + steps: + - intent: review_last_activity + - action: action_set_slot_general_activity + - action: utter_greeting #4 + - checkpoint: rescheduling_query_dialog + + +- story: continue after rescheduling first execution + steps: + - checkpoint: continue_with_general_activity + - action: check_if_first_execution_ga + - slot_was_set: + - general_activity_first_execution: true + - checkpoint: choose_activity_checkpoint + +- story: continue after rescheduling not first time + steps: + - checkpoint: continue_with_general_activity + - action: check_if_first_execution_ga + - slot_was_set: + - general_activity_first_execution: false + - action: get_last_performed_activity + - action: activity_usefulness_form + - active_loop: activity_usefulness_form + - active_loop: null + - action: general_activity_check_rating + - checkpoint: low_or_high_rating + +## 2a: The rating of the activity is in the top 5 + +- story: if rating high then check if this activity requires text input + steps: + - checkpoint: low_or_high_rating + - slot_was_set: + - general_activity_low_high_rating: high + - action: utter_general_activity_thanks_top_5_1 + - action: check_user_input_required + - checkpoint: input_required_check + +## 3b: The rating of the activity is in the top 5 and input of the user is not required + +- story: if input is not required + steps: + - checkpoint: input_required_check + - slot_was_set: + - is_user_input_required: false + - action: utter_general_activity_look_back + - checkpoint: choose_activity_checkpoint + +## 3c: The input of the user is required and already given + +- story: if input is required and previously given + steps: + - checkpoint: input_required_check + - slot_was_set: + - is_user_input_required: true + - action: check_activity_done + - checkpoint: has_activity_already_done + +## 3a: the activity has not been done before + +- story: if input is required and previously given + steps: + - checkpoint: has_activity_already_done + - slot_was_set: + - is_activity_done: false + - checkpoint: input_activity_description + +## 3c: the activity has been already done before + +- story: if input is required and previously given + steps: + - checkpoint: has_activity_already_done + - slot_was_set: + - is_activity_done: true + - action: get_activity_user_input + - action: utter_general_activity_thanks_top_5_2 + - action: save_or_edit_form + - active_loop: save_or_edit_form + - active_loop: null + - checkpoint: save_or_edit_checkpoint + +## 4b: save option selected + +- story: if the user decides to save the previous content + steps: + - checkpoint: save_or_edit_checkpoint + - slot_was_set: + - save_or_edit_slot: 1 + - action: utter_general_activity_save + - checkpoint: choose_activity_checkpoint + +## 4a: edit option selected + +- story: if the user decides to edit the previous content + steps: + - checkpoint: save_or_edit_checkpoint + - slot_was_set: + - save_or_edit_slot: 2 + - action: utter_general_activity_edit_1 + - checkpoint: input_activity_description + +## 4a: input the description + +- story: if the user decides to edit the previous content + steps: + - checkpoint: input_activity_description + - action: utter_general_activity_edit_2 + - action: general_activity_description_form + - active_loop: general_activity_description_form + - active_loop: null + - action: save_description_in_db + - action: utter_general_activity_edit_3 + - checkpoint: choose_activity_checkpoint + +## 2b: The rating of the activity is not in the top 5 + +- story: if rating is low then just thanks + steps: + - checkpoint: low_or_high_rating + - slot_was_set: + - general_activity_low_high_rating: low + - action: utter_general_activity_thanks #9 + - checkpoint: choose_activity_checkpoint + +## 6a: the user chooses the activity + +- story: new activities are randomly proposed to the user + steps: + - checkpoint: choose_activity_checkpoint + - action: check_who_decides + - slot_was_set: + - who_decides_slot: user + - action: get_three_random_activities + - action: utter_general_activity_choose_next_activity + - action: general_activity_next_activity_form + - active_loop: general_activity_next_activity_form + - active_loop: null + - action: utter_general_activity_next_user + - action: load_activity + - action: action_end_dialog + +## 7a: the coach chooses the activity + +- story: new activities are randomly proposed to the user + steps: + - checkpoint: choose_activity_checkpoint + - action: check_who_decides + - slot_was_set: + - who_decides_slot: coach + - action: get_activity_coach_choice + - action: utter_general_activity_next_coach + - action: load_activity + - action: action_end_dialog diff --git a/Rasa_Bot/data/stories_rescheduling_dialog.yml b/Rasa_Bot/data/stories_rescheduling_dialog.yml index 59f26a06..6cb3544e 100644 --- a/Rasa_Bot/data/stories_rescheduling_dialog.yml +++ b/Rasa_Bot/data/stories_rescheduling_dialog.yml @@ -13,10 +13,8 @@ stories: - rescheduling_now: true - active_loop: null - action: utter_user_chose_now - - action: action_reset_rescheduling_now_slot - - slot_was_set: - - rescheduling_now: null - - checkpoint: continue_with_future_self_dialog_after_rescheduling_query + - action: action_set_continuation + - checkpoint: continue_dialog_checkpoint # Rescheduling query - later @@ -29,9 +27,6 @@ stories: - rescheduling_now: false - active_loop: null - action: utter_user_chose_later - - action: action_reset_rescheduling_now_slot - - slot_was_set: - - rescheduling_now: null - action: action_get_rescheduling_options_list - action: rescheduling_options_form - active_loop: rescheduling_options_form # TODO: use rescheduling_option slot to reschedule @@ -43,4 +38,18 @@ stories: - rescheduling_option: null - action: action_end_dialog # TODO: what are we going to do at the end of the dialog if the user types something again? - # Maybe a ConversationPaused() event from custom action? \ No newline at end of file + # Maybe a ConversationPaused() event from custom action? + +- story: get to general activity dialog + steps: + - checkpoint: continue_dialog_checkpoint + - slot_was_set: + - dialog_to_continue: general_activity + - checkpoint: continue_with_general_activity + +- story: get to future self dialog + steps: + - checkpoint: continue_dialog_checkpoint + - slot_was_set: + - dialog_to_continue: future_self_preparation + - checkpoint: continue_with_future_self_dialog_after_rescheduling_query diff --git a/Rasa_Bot/domain/domain_execution_dialogs.yml b/Rasa_Bot/domain/domain_execution_dialogs.yml index 82263377..4374f731 100644 --- a/Rasa_Bot/domain/domain_execution_dialogs.yml +++ b/Rasa_Bot/domain/domain_execution_dialogs.yml @@ -8,6 +8,7 @@ slots: current_intervention_component: type: text initial_value: 'NONE' + influence_conversation: false mappings: - type: custom diff --git a/Rasa_Bot/domain/domain_general_activity.yml b/Rasa_Bot/domain/domain_general_activity.yml new file mode 100644 index 00000000..02a38101 --- /dev/null +++ b/Rasa_Bot/domain/domain_general_activity.yml @@ -0,0 +1,204 @@ +intents: +- review_last_activity + + +slots: + # Check if it is the first time the dialog is performed + general_activity_first_execution: + type: bool + influence_conversation: true + mappings: + - type: custom + # The user's rating of previous activity + activity_useful_rating: + type: float + influence_conversation: false + mappings: + - type: from_text + conditions: + - active_loop: activity_usefulness_form + + general_activity_low_high_rating: + type: categorical + values: + - low + - high + influence_conversation: true + mappings: + - type: custom + + # to check if the user's input is required + is_user_input_required: + type: bool + influence_conversation: true + mappings: + - type: custom + + # to check if the user had done the activity before + is_activity_done: + type: bool + influence_conversation: true + mappings: + - type: custom + + # name of the selected activity + activity_user_input: + type: text + influence_conversation: false + mappings: + - type: custom + + # save (true) or edit (false) the text + save_or_edit_slot: + type: categorical + values: + - 1 + - 2 + influence_conversation: true + mappings: + - type: from_text + conditions: + - active_loop: save_or_edit_form + + # content of the description + general_activity_description_slot: + type: text + influence_conversation: false + mappings: + - type: from_text + conditions: + - active_loop: general_activity_description_form + + # name of last performed activity + last_activity_slot: + type: text + influence_conversation: false + mappings: + - type: custom + + # ID of last performed activity as stored in the DB + last_activity_id_slot: + type: float + influence_conversation: false + mappings: + - type: custom + + # name of first proposed next activity + activity1_name: + type: text + influence_conversation: false + mappings: + - type: custom + # name of second proposed next activity + activity2_name: + type: text + influence_conversation: false + mappings: + - type: custom + # name of third proposed next activity + activity3_name: + type: text + influence_conversation: false + mappings: + - type: custom + # ids of the three random activities + rnd_activities_ids: + type: any + influence_conversation: false + mappings: + - type: custom + # name of chosen activity + chosen_activity_slot: + type: text + influence_conversation: false + mappings: + - type: custom + + # options for choosing the next activity + general_activity_next_activity_slot: + type: text + influence_conversation: false + mappings: + - type: from_text + conditions: + - active_loop: general_activity_next_activity_form + + who_decides_slot: + type: categorical + values: + - user + - coach + influence_conversation: true + mappings: + - type: custom + +forms: + # this form is for rating the most recent activity + activity_usefulness_form: + required_slots: + - activity_useful_rating + ignored_intents: + - review_last_activity + # ask if the user wants to save or edit the text + save_or_edit_form: + required_slots: + - save_or_edit_slot + # ask the user to input the description for the EHBO + general_activity_description_form: + required_slots: + - general_activity_description_slot + general_activity_next_activity_form: + required_slots: + - general_activity_next_activity_slot + + +responses: + utter_ask_activity_useful_rating: #6 + - text: "Laten we eerst terugkijken op je vorige oefening. Hoe nuttig vond je de oefening '{last_activity_slot}'? Dit kun je aangeven op een schaal van 0-10. 0 is 'helemaal niet nuttig'. 10 is 'heel erg nuttig'. Typ nu een cijfer van 0 - 10." + utter_general_activity_thanks: + - text: "Bedankt voor je feedback." + utter_general_activity_look_back: + - text: "Hier kun je naar terugkijken wanneer je het moeilijk hebt." + utter_general_activity_thanks_top_5_1: + - text: "Bedankt voor je feedback. Je vindt deze activiteit nuttig. Daarom wordt deze toegevoegd aan je EHBO-doos." +# utter_what_you_learned_from_activity: + utter_general_activity_thanks_top_5_2: + - text: "Deze oefening heb je al eerder als nuttig beoordeeld. Je liet mij toen weten dat je het volgende geleerd hebt van de oefening: {activity_user_input}" + utter_ask_save_or_edit_slot: + - text: "Wil je deze tekst weer bewaren in de EHBO-doos of wil je de tekst aanpassen? Typ 1 voor 'Bewaren', Typ 2 voor 'Aanpassen'. " + utter_general_activity_save: + - text: "Super! Dezelfde tekst wordt nu opgeslagen in de EHBO-doos. Hier kun je naar terugkijken wanneer je het moeilijk hebt. . " + utter_general_activity_edit_1: + - text: "Prima! " + utter_general_activity_edit_2: + - text: "Kun je in een paar zinnen vertellen wat je geleerd hebt van deze oefening? Dus wat je wilt onthouden van deze oefening? Deze zinnen zullen uiteindelijk in je EHBO-doos terechtkomen. " + utter_general_activity_edit_3: + - text: "Bedankt! Jouw antwoord wordt nu opgeslagen en toegevoegd aan de EHBO-doos. Hier kun je naar terugkijken wanneer je het moeilijk hebt. " + utter_ask_general_activity_description_slot: + - text: "Typ nu een paar zinnen over wat je geleerd hebt, zodat ik dit kan toevoegen aan je EHBO-doos. " + utter_general_activity_choose_next_activity: + - text: "Dan is het nu tijd voor een nieuwe oefening. Je kunt kiezen uit drie oefeningen." + utter_ask_general_activity_next_activity_slot: + - text: "Welke oefening wil je doen? Typ 1 als je '{activity1_name}' wilt doen. Typ 2 als je '{activity2_name}' wilt doen. Typ 3 als je '{activity3_name}' wilt doen. Typ 4 als je wilt kiezen uit drie andere oefeningen." + utter_general_activity_next_user: + - text: "Oké, goede keuze! Veel succes met de oefening." + utter_general_activity_next_coach: + - text: "Dan is het nu tijd voor een nieuwe oefening. Ik stel voor om de oefening {chosen_activity_slot} te doen. Veel succes! " + +actions: + - check_if_first_execution_ga # Check if it is the first time the dialog is performed + - general_activity_check_rating # sets general_activity_low_high_rating to high or low + - check_user_input_required # check if the selected activity requires the input of the user + - check_activity_done # check if the activity selected has been already done + - get_activity_user_input # write to the activity_user_input slot the iput for the chosen activity + - validate_save_or_edit_form # validate the slot save_or_edit_slot + - validate_general_activity_description_form # check if the description has at least 50 characters + - save_description_in_db # saves the provided activity description in DB + - get_three_random_activities # get three random activities from the resources sets activity1_name - activity3_name + - validate_general_activity_next_activity_form + - get_last_performed_activity # get the last performed activity + - get_activity_coach_choice # the coach chooses the next activity + - validate_activity_usefulness_form # validate the slot activity_useful_rating + - check_who_decides # determines if the user can decide or if the coach decides + - action_set_slot_general_activity # set the slot to the current running dialog + - load_activity # load the activity instructions diff --git a/Rasa_Bot/domain/domain_generic.yml b/Rasa_Bot/domain/domain_generic.yml index 3b8fcfbe..f5f62f0f 100644 --- a/Rasa_Bot/domain/domain_generic.yml +++ b/Rasa_Bot/domain/domain_generic.yml @@ -39,12 +39,16 @@ responses: - text: "Je kunt deze woorden gebruiken om met mij te praten: 1) EHBO doos, 2) ..., ... ." ### Re-prompts in forms + utter_please_answer_1_2: + - text: "Antwoord alsjeblieft met '1,' of '2.'" utter_please_answer_1_2_3: - text: "Antwoord alsjeblieft met '1,' '2,' of '3.'" utter_please_answer_1_2_3_4: - text: "Antwoord alsjeblieft met '1,' '2,' '3,' of '4.'" utter_please_answer_1_to_5: - text: "Kun je een geheel getal tussen 1 en 5 opgeven? ..." + utter_please_answer_0_to_10: + - text: "Kun je een geheel getal tussen 0 en 10 opgeven? ..." utter_please_answer_yes_no: - text: "Geef alsjeblieft antwoord met 'ja' of 'nee'?" utter_please_answer_now_or_later: @@ -52,4 +56,8 @@ responses: utter_please_answer_more_words: - text: "Zou je dat in meer woorden kunnen omschrijven?" utter_did_not_understand: - - text: "Hmm ik heb dat niet begrepen." \ No newline at end of file + - text: "Hmm ik heb dat niet begrepen." + utter_greeting: + - text: "Hoi!" + utter_give_more_details: + - text: "Kun je dat wat uitgebreider uitleggen?" \ No newline at end of file diff --git a/Rasa_Bot/domain/domain_rescheduling_dialog.yml b/Rasa_Bot/domain/domain_rescheduling_dialog.yml index 75c1cb17..87170a82 100644 --- a/Rasa_Bot/domain/domain_rescheduling_dialog.yml +++ b/Rasa_Bot/domain/domain_rescheduling_dialog.yml @@ -29,6 +29,16 @@ slots: influence_conversation: false mappings: - type: custom + + # Set the dialog to be resumed + dialog_to_continue: + type: categorical + values: + - general_activity + - future_self_preparation + influence_conversation: true + mappings: + - type: custom responses: ### General rescheduling query dialog @@ -55,6 +65,7 @@ actions: - validate_rescheduling_options_form - action_reset_rescheduling_option_slot - action_reschedule_dialog + - action_set_continuation forms: rescheduling_now_or_later_form: diff --git a/Rasa_Bot/models/20221014-151911-hot-dodecagon.tar.gz b/Rasa_Bot/models/20221116-120859-ordered-chateau.tar.gz similarity index 68% rename from Rasa_Bot/models/20221014-151911-hot-dodecagon.tar.gz rename to Rasa_Bot/models/20221116-120859-ordered-chateau.tar.gz index 636881ad..5d5cc7ff 100644 Binary files a/Rasa_Bot/models/20221014-151911-hot-dodecagon.tar.gz and b/Rasa_Bot/models/20221116-120859-ordered-chateau.tar.gz differ