Exemplo n.º 1
0
    def _bkt(self) -> Bkt:
        pL = state_db.get(state_db.Keys.BKT_pL)
        pT = state_db.get(state_db.Keys.BKT_pT)
        pS = state_db.get(state_db.Keys.BKT_pS)
        pG = state_db.get(state_db.Keys.BKT_pG)

        return Bkt(pL0=pL, pT=pT, pS=pS, pG=pG)
Exemplo n.º 2
0
    class Messages:

        big_5_question = Message(
            content=("How do you feel about the following statement? " +
                     "'{'var': 'big_5_question', 'index': " +
                     "'{'db': '%s', 'post-op': 'increment'}'}'" %
                     state_db.Keys.PSYCH_QUESTION_INDEX),
            options=[
                'Strongly agree',
                'Agree',
                'Neutral',
                'disagree',
                'Strongly disagree',
            ],
            message_type=Message.Type.MULTIPLE_CHOICE,
            result_db_key=state_db.Keys.PSYCH_QUESTION_ANSWERS,
            is_append_result=True,
            text_populator=text_populator,
        )
        when_question = Message(
            content="{when_question}",
            options='is when',
            message_type=Message.Type.TIME_ENTRY,
            args=[
                '15',
                lambda: "{'db': '%s'}" % state_db.Keys.WALK_TIME,
            ],
            result_db_key=state_db.Keys.WALK_TIME,
            result_convert_from_str_fn=lambda x: datetime.datetime.strptime(
                x, '%I:%M %p').time(),
            tests=lambda x: (state_db.get(state_db.Keys.AM_CHECKIN_TIME) <= x
                             <= state_db.get(state_db.Keys.PM_CHECKIN_TIME)),
            error_message=
            "Please pick a time after now and before our evening checkin",
            text_populator=text_populator,
        )
        set_goal = Message(
            content=("I suggest that you do {'db': '%s'} steps today. " %
                     state_db.Keys.SUGGESTED_STEPS_TODAY +
                     "How many steps would you like to do today?"),
            options='steps',
            args=[
                lambda: "{'db': '%s'}" % state_db.Keys.
                MIN_SUGGESTED_STEPS_TODAY,
                lambda: "{'db': '%s'}" % state_db.Keys.
                MAX_SUGGESTED_STEPS_TODAY,
                '1',
                lambda: "{'db': '%s'}" % state_db.Keys.SUGGESTED_STEPS_TODAY,
            ],
            message_type=Message.Type.SLIDER,
            result_convert_from_str_fn=int,
            result_db_key=state_db.Keys.STEPS_GOAL,
            tests=lambda x: x >= state_db.get(state_db.Keys.
                                              MIN_SUGGESTED_STEPS_TODAY),
            error_message=(
                "Please select a goal that is at least {'db': '%s'} steps" %
                state_db.Keys.MIN_SUGGESTED_STEPS_TODAY, ),
            text_populator=text_populator,
        )
    def test_check_first_run(self):

        self.assertFalse(state_db.is_set(state_db.Keys.FIRST_MEETING))
        self.abm_interaction.set_prompt_to_handle()
        self.abm_interaction.run_scheduler_once()
        self.assertTrue(state_db.is_set(state_db.Keys.FIRST_MEETING))
        self.assertTrue(state_db.get(state_db.Keys.IS_DONE_AM_CHECKIN_TODAY))
        self.assertFalse(state_db.get(state_db.Keys.IS_DONE_PM_CHECKIN_TODAY))
    def _build_checkin_schedule(self):

        am_checkin_time = state_db.get(state_db.Keys.AM_CHECKIN_TIME)
        pm_checkin_time = state_db.get(state_db.Keys.PM_CHECKIN_TIME)

        self._checkin_scheduler.clear()
        self._checkin_scheduler.every().day.at(
            self._time_to_schedule_str(am_checkin_time)).do(
                self._run_scheduled_if_still_open)
        self._checkin_scheduler.every().day.at(
            self._time_to_schedule_str(pm_checkin_time)).do(
                self._run_scheduled_if_still_open)
    def test_prompt_interactions(self, _):
        """
        Note that the steps goals and numbers get in a weird positive feedback loop because of how I
        monkey patched the fitbit reader
        """

        num_days = param_db.get(param_db.Keys.WEEKS_WITH_ROBOT)*7

        initial_datetime = datetime.datetime(
            year=2019, month=1, day=1, hour=8, minute=6, second=3
        )
        with freeze_time(initial_datetime) as frozen_datetime:
            state_db.reset()
            self.abm_interaction = AbmInteraction()

            self.update_after_first_meeting()

            pm_checkin_datetime = self.get_pm_checkin_datetime(0, initial_datetime)
            frozen_datetime.move_to(pm_checkin_datetime)
            self.update_day_steps()
            self.abm_interaction.set_prompt_to_handle()
            self.abm_interaction.run_scheduler_once()
            self.update_after_am_checkin()

            self.assertTrue(state_db.is_set(state_db.Keys.FIRST_MEETING))
            self.assertTrue(state_db.get(state_db.Keys.IS_DONE_AM_CHECKIN_TODAY))
            self.assertTrue(state_db.get(state_db.Keys.IS_DONE_PM_CHECKIN_TODAY))
            self.assertTrue(state_db.get(state_db.Keys.IS_MET_GOAL))

            for day in range(1, num_days + 2):

                am_checkin_datetime = self.get_am_checkin_datetime(day, initial_datetime)
                frozen_datetime.move_to(am_checkin_datetime)
                self.abm_interaction._new_day_update()
                self.abm_interaction.set_prompt_to_handle()
                self.abm_interaction.run_scheduler_once()
                self.update_after_am_checkin()

                # Run non consequential off-checkin
                for _ in range(2):
                    self.abm_interaction.set_prompt_to_handle()
                    self.abm_interaction.run_scheduler_once()

                self.assertTrue(state_db.get(state_db.Keys.IS_DONE_AM_CHECKIN_TODAY))
                self.assertFalse(state_db.get(state_db.Keys.IS_DONE_PM_CHECKIN_TODAY))

                pm_checkin_datetime = self.get_pm_checkin_datetime(day, initial_datetime)
                frozen_datetime.move_to(pm_checkin_datetime)
                self.update_day_steps()
                self.abm_interaction.set_prompt_to_handle()
                self.abm_interaction.run_scheduler_once()

                self.assertTrue(state_db.get(state_db.Keys.IS_DONE_AM_CHECKIN_TODAY))
                self.assertTrue(state_db.get(state_db.Keys.IS_DONE_PM_CHECKIN_TODAY))
                self.assertTrue(state_db.get(state_db.Keys.IS_MET_GOAL))

                # Run non consequential off-checkin
                for _ in range(2):
                    self.abm_interaction._build_and_run_plan()
    def _new_day_update(self):

        last_day_update = state_db.get(state_db.Keys.LAST_DAY_UPDATE_DATE)
        current_date = datetime.datetime.now().date()
        days_since_last_day_update = (current_date - last_day_update).days

        if days_since_last_day_update > 0:

            self._update_week_steps_and_goals()
            if not state_db.get(state_db.Keys.IS_DONE_PM_CHECKIN_TODAY):
                state_db.set(state_db.Keys.IS_MISSED_PM_YESTERDAY, True)
            else:
                state_db.set(state_db.Keys.IS_MISSED_PM_YESTERDAY, False)

            self._publish_automaticity()

            state_db.set(state_db.Keys.IS_DONE_AM_CHECKIN_TODAY, False)
            state_db.set(state_db.Keys.IS_DONE_PM_CHECKIN_TODAY, False)

            state_db.set(state_db.Keys.LAST_DAY_UPDATE_DATE,
                         datetime.datetime.now().date())
 def get_am_checkin_datetime(day, initial_datetime):
     am_checkin_time = state_db.get(state_db.Keys.AM_CHECKIN_TIME)
     am_checkin_date_time = (
         (
                 initial_datetime
                 + datetime.timedelta(days=day)
         ).replace(
             hour=am_checkin_time.hour,
             minute=am_checkin_time.minute,
         )
     )
     return am_checkin_date_time
Exemplo n.º 8
0
    def test_bkt_operations(self):
        automaticity = self.builder._automaticity
        # True because of adding epsilon to make not degenerate
        self.assertLessEqual(state_db.get(state_db.Keys.BKT_pL), automaticity)

        old_pL = automaticity
        for _ in range(100):
            self.builder._bkt_update_pL(True)
            new_pL = self.builder._automaticity
            self.assertLessEqual(old_pL, new_pL)
            old_pL = new_pL
        self.assertLess(automaticity, old_pL)
Exemplo n.º 9
0
    def _build_off_checkin(self, planner=None):

        if planner is None:
            planner = MessagerPlanner(possible_plans)

        if self._is_time_for_status_update():
            if self._is_synced_recently():
                if state_db.get(state_db.Keys.STEPS_TODAY) >= state_db.get(state_db.Keys.STEPS_GOAL):
                    planner.insert(OffCheckin.Messages.give_status_met_goal)
                else:
                    planner.insert(OffCheckin.Messages.give_status)
            else:
                planner.insert(OffCheckin.Messages.no_sync)

        planner.insert(
            Options.options,
            post_hook=lambda: state_db.set(
                state_db.Keys.IS_REDO_SCHEDULE, True
            )
        )

        return planner
    def update_after_first_meeting(self):
        state_db.set(state_db.Keys.FIRST_MEETING, datetime.datetime.now())
        state_db.set(state_db.Keys.IS_DONE_AM_CHECKIN_TODAY, True)
        state_db.set(state_db.Keys.IS_DONE_PM_CHECKIN_TODAY, False)
        state_db.set(state_db.Keys.IS_MISSED_PM_YESTERDAY, False)
        state_db.set(state_db.Keys.AM_CHECKIN_TIME, datetime.time(8, 30))
        state_db.set(state_db.Keys.PM_CHECKIN_TIME, datetime.time(20, 30))
        state_db.set(state_db.Keys.DAY_OFF, 'saturday')
        state_db.set(state_db.Keys.USER_NAME, 'John')
        state_db.set(state_db.Keys.STEPS_GOAL, state_db.get(state_db.Keys.SUGGESTED_STEPS_TODAY))

        self.abm_interaction._build_checkin_schedule()
        state_db.set(state_db.Keys.IS_REDO_SCHEDULE, False)
Exemplo n.º 11
0
    def _build_and_run_plan(self):
        plan = self._plan_builder.build()
        interaction_engine = InteractionEngine(self._interface, plan,
                                               possible_plans)
        try:
            self._publish_is_record_msg(True)
            interaction_engine.run()
        except TimeoutError:
            pass
        finally:
            self._publish_is_record_msg(False)

        if state_db.get(state_db.Keys.IS_REDO_SCHEDULE):
            self._build_checkin_schedule()
            state_db.set(state_db.Keys.IS_REDO_SCHEDULE, False)
 def update_day_steps(self, steps_diff_from_goal=0):
     self.set_steps_per_day(
         state_db.get(state_db.Keys.STEPS_GOAL) + steps_diff_from_goal
     )
     self.abm_interaction._update_todays_steps()
 def update_after_am_checkin(self):
     state_db.set(state_db.Keys.STEPS_GOAL, state_db.get(state_db.Keys.SUGGESTED_STEPS_TODAY))
Exemplo n.º 14
0

if __name__ == '__main__' and False:

    IS_RESET_STATE_DB = False
    interaction = AbmInteraction(is_reset_state_db=IS_RESET_STATE_DB)
    mins_before_allowed = param_db.get(param_db.Keys.MINS_BEFORE_ALLOW_CHECKIN)
    mins_after_allowed = param_db.get(param_db.Keys.MINS_AFTER_ALLOW_CHECKIN)

    print("FIRST INTERACTION")
    with freeze_time("2019-11-1 8:05:00"):
        interaction._build_and_run_plan()

    print("OFF CHECKIN")
    interaction._update_todays_steps()
    checkin_time = state_db.get(state_db.Keys.AM_CHECKIN_TIME)
    with freeze_time(
            f"2019-11-1 {checkin_time.hour:02d}:{checkin_time.minute:02d}:00"):
        interaction._build_and_run_plan()

    print("PM CHECKIN - Fail")
    interaction._update_todays_steps()
    state_db.set(state_db.Keys.STEPS_TODAY,
                 state_db.get(state_db.Keys.STEPS_GOAL) - 1)
    checkin_time = state_db.get(state_db.Keys.PM_CHECKIN_TIME)
    with freeze_time(
            f"2019-11-1 {checkin_time.hour:02d}:{checkin_time.minute:02d}:00"):
        interaction._build_and_run_plan()

    print("OFF CHECKIN")
    checkin_time = state_db.get(state_db.Keys.PM_CHECKIN_TIME)
Exemplo n.º 15
0
 def _steps_today(self):
     return state_db.get(state_db.Keys.STEPS_TODAY)
Exemplo n.º 16
0
 def _goal_today(self):
     return state_db.get(state_db.Keys.STEPS_GOAL)
Exemplo n.º 17
0
 def _is_missed_pm_yesterday(self):
     return state_db.get(state_db.Keys.IS_MISSED_PM_YESTERDAY)
Exemplo n.º 18
0
 def _is_missed_am_checkin(self):
     return (
             not state_db.get(state_db.Keys.IS_DONE_AM_CHECKIN_TODAY)
             and datetime.datetime.now() > self._am_checkin_datetime +
             datetime.timedelta(minutes=self._mins_after_checkin_allowed)
     )
Exemplo n.º 19
0
 def _is_done_pm_checkin_today(self):
     return state_db.get(state_db.Keys.IS_DONE_PM_CHECKIN_TODAY)
Exemplo n.º 20
0
class AmCheckin:
    class Messages:

        big_5_question = Message(
            content=("How do you feel about the following statement? " +
                     "'{'var': 'big_5_question', 'index': " +
                     "'{'db': '%s', 'post-op': 'increment'}'}'" %
                     state_db.Keys.PSYCH_QUESTION_INDEX),
            options=[
                'Strongly agree',
                'Agree',
                'Neutral',
                'disagree',
                'Strongly disagree',
            ],
            message_type=Message.Type.MULTIPLE_CHOICE,
            result_db_key=state_db.Keys.PSYCH_QUESTION_ANSWERS,
            is_append_result=True,
            text_populator=text_populator,
        )
        when_question = Message(
            content="{when_question}",
            options='is when',
            message_type=Message.Type.TIME_ENTRY,
            args=[
                '15',
                lambda: "{'db': '%s'}" % state_db.Keys.WALK_TIME,
            ],
            result_db_key=state_db.Keys.WALK_TIME,
            result_convert_from_str_fn=lambda x: datetime.datetime.strptime(
                x, '%I:%M %p').time(),
            tests=lambda x: (state_db.get(state_db.Keys.AM_CHECKIN_TIME) <= x
                             <= state_db.get(state_db.Keys.PM_CHECKIN_TIME)),
            error_message=
            "Please pick a time after now and before our evening checkin",
            text_populator=text_populator,
        )
        set_goal = Message(
            content=("I suggest that you do {'db': '%s'} steps today. " %
                     state_db.Keys.SUGGESTED_STEPS_TODAY +
                     "How many steps would you like to do today?"),
            options='steps',
            args=[
                lambda: "{'db': '%s'}" % state_db.Keys.
                MIN_SUGGESTED_STEPS_TODAY,
                lambda: "{'db': '%s'}" % state_db.Keys.
                MAX_SUGGESTED_STEPS_TODAY,
                '1',
                lambda: "{'db': '%s'}" % state_db.Keys.SUGGESTED_STEPS_TODAY,
            ],
            message_type=Message.Type.SLIDER,
            result_convert_from_str_fn=int,
            result_db_key=state_db.Keys.STEPS_GOAL,
            tests=lambda x: x >= state_db.get(state_db.Keys.
                                              MIN_SUGGESTED_STEPS_TODAY),
            error_message=(
                "Please select a goal that is at least {'db': '%s'} steps" %
                state_db.Keys.MIN_SUGGESTED_STEPS_TODAY, ),
            text_populator=text_populator,
        )

    where_graph = most_recent_options_graph(
        "ask where",
        "{where_question}",
        options=lambda: state_db.get(state_db.Keys.WALK_PLACES),
        max_num_options=param_db.get(param_db.Keys.NUM_OPTIONS_TO_DISPLAY),
        save_db_key=state_db.Keys.WALK_PLACES,
        text_populator=text_populator,
        new_entry_text_choice='Somewhere else',
        new_entry_message="So where will you walk?",
        new_entry_options="Is where",
        tests=lambda x: len(x) > 1,
        new_entry_error_message="Please write more than one letter")
    how_remember_graph = most_recent_options_graph(
        "how_remember",
        "{how_question_forget}",
        options=lambda: state_db.get(state_db.Keys.HOW_REMEMBER),
        max_num_options=param_db.get(param_db.Keys.NUM_OPTIONS_TO_DISPLAY),
        save_db_key=state_db.Keys.HOW_REMEMBER,
        text_populator=text_populator,
        new_entry_text_choice='Something else',
        tests=lambda x: len(x) > 1,
        new_entry_error_message="Please write more than one letter")
    how_busy_graph = most_recent_options_graph(
        "how_busy",
        "{how_question_busy}",
        options=lambda: state_db.get(state_db.Keys.HOW_BUSY),
        max_num_options=param_db.get(param_db.Keys.NUM_OPTIONS_TO_DISPLAY),
        save_db_key=state_db.Keys.HOW_BUSY,
        text_populator=text_populator,
        new_entry_text_choice='Something else',
        tests=lambda x: len(x) > 1,
        new_entry_error_message="Please write more than one letter")
    how_motivated_graph = most_recent_options_graph(
        "how_motivated",
        "{how_question_not_motivated}",
        options=lambda: state_db.get(state_db.Keys.HOW_MOTIVATE),
        max_num_options=param_db.get(param_db.Keys.NUM_OPTIONS_TO_DISPLAY),
        save_db_key=state_db.Keys.HOW_MOTIVATE,
        text_populator=text_populator,
        new_entry_text_choice='Something else',
        tests=lambda x: len(x) > 1,
        new_entry_error_message="Please write more than one letter")
Exemplo n.º 21
0
 def _publish_automaticity(self):
     automaticity = state_db.get(state_db.Keys.BKT_pL)
     rospy.loginfo("Publishing automaticity: {}".format(automaticity))
     self._automaticity_publisher.publish(Float32(automaticity))
Exemplo n.º 22
0
 def _is_synced_recently(self):
     last_fitbit_sync = state_db.get(state_db.Keys.LAST_FITBIT_SYNC)
     max_mins_since_sync = param_db.get(param_db.Keys.MINS_BEFORE_WARNING_ABOUT_FITBIT_NOT_SYNCING)
     is_synced_recently = datetime.datetime.now() < last_fitbit_sync + datetime.timedelta(
         minutes=max_mins_since_sync)
     return is_synced_recently
Exemplo n.º 23
0
    def __init__(
        self,
        credentials_file_path="fitbit_credentials.yaml",
        redirect_url="http://localhost",
        is_data_recording_topic='data_capture/is_record',
        automaticity_topic='abm/automaticity',
        is_go_to_sleep_topic='cordial/sleep',
        interface=None,
        is_reset_state_db=False,
        goal_setter=None,
    ):

        if is_reset_state_db:
            logging.info("Reseting database")
            state_db.reset()

        self._plan_builder = PlanBuilder()
        if interface is None:
            interface = TerminalClientAndServerInterface(state_db)
        self._interface = interface
        self._fitbit = AbmFitbitClient(
            credentials_file_path=credentials_file_path,
            redirect_url=redirect_url,
        )

        start_days_before_first_meeting = datetime.timedelta(days=7)
        if state_db.is_set(state_db.Keys.FIRST_MEETING):
            start_date = state_db.get(
                state_db.Keys.FIRST_MEETING) - start_days_before_first_meeting
        else:
            start_date = datetime.datetime.now(
            ) - start_days_before_first_meeting

        if goal_setter is None:
            logging.info('Creating goal setter')
            goal_setter = GoalSetter(
                fitbit_active_steps_fn=self._fitbit.get_total_steps,
                start_date=start_date,
                num_weeks=param_db.get(param_db.Keys.WEEKS_WITH_ROBOT) +
                1,  # +1 for week with just fitbit
                final_week_goal=param_db.get(param_db.Keys.FINAL_STEPS_GOAL),
                min_weekly_steps_goal=param_db.get(
                    param_db.Keys.MIN_WEEKLY_STEPS_GOAL),
                week_goal_min_improvement_ratio=1.1,
                week_goal_max_improvement_ratio=2.0,
                daily_goal_min_to_max_ratio=2.5,
            )
        self._goal_setter = goal_setter

        self._is_recording_publisher = rospy.Publisher(is_data_recording_topic,
                                                       Bool,
                                                       queue_size=1)
        self._automaticity_publisher = rospy.Publisher(automaticity_topic,
                                                       Float32,
                                                       queue_size=1)
        self._is_go_to_sleep_publisher = rospy.Publisher(is_go_to_sleep_topic,
                                                         Bool,
                                                         queue_size=1)

        self._update_week_steps_and_goals()
        self._update_todays_steps()

        self._checkin_scheduler = schedule.Scheduler()

        self._update_scheduler = schedule.Scheduler()
        self._update_scheduler.every(15).seconds.do(self._new_day_update)

        self._is_prompt_to_run = False
        if state_db.is_set(state_db.Keys.FIRST_MEETING):
            self._build_checkin_schedule()
            state_db.set(state_db.Keys.IS_REDO_SCHEDULE, False)
        else:
            self._init_vars()
Exemplo n.º 24
0
 def _pm_checkin_time(self):
     return state_db.get(state_db.Keys.PM_CHECKIN_TIME)
Exemplo n.º 25
0
class FirstMeeting:
    class Messages:
        introduce_self = Message(
            content=
            ("*QT/hi*Nice to meet you, {'db': '%s'}. " %
             state_db.Keys.USER_NAME +
             "*QT/show_QT*My name is QT. I'm a robot made by LuxAI in Luxembourg, a country in Europe. "
             + "I'm going to try to help you walk more regularly. " +
             "I won't go on walks with you*wink*. " +
             "Instead, I'll ask you questions about your plans to walk and " +
             "keep track of your daily steps by wirelessly talking to your Fitbit watch. "
             ),
            message_type=Message.Type.MULTIPLE_CHOICE,
            text_populator=text_populator,
            options=['Sure thing!', 'Sure', 'No thanks'],
        )
        explain_checkin = Message(
            content=
            ("To help you walk regularly, *QT/bored* we'll talk at least twice a day: "
             + "once in the morning and once in the evening. " +
             "In the morning, we'll set a walking goal for the day and I'll ask you some questions. "
             +
             "These questions will be about your plans to walk or about you. "
             +
             "In the evening, we'll review the day. If you don't meet your walking goal, I may ask you why. "
             ),
            options='Sounds good',
            message_type=Message.Type.MULTIPLE_CHOICE,
            text_populator=text_populator,
        )
        explain_off_checkin = Message(
            content=(
                "You can also talk to me any other time. " +
                # "I'll be happy to see you. *nod* I can tell you jokes or tell you good things. " +
                # "You can ask me to introduce myself, if you'd like me to meet someone new. " +
                # "I can also explain how we'll work together again. " +
                "Lastly, you can change what I call you or when we have our morning and evening checkins. "
            ),
            options='Okay',
            message_type=Message.Type.MULTIPLE_CHOICE,
            text_populator=text_populator,
        )
        explain_fitbit = Message(
            content=
            ("Now, I'll tell you about how I'll work with your Fitbit watch. "
             + "This part is important.*nod* " +
             "To know how much you walk, I'll talk to your Fitbit watch. " +
             "Unfortunately, this can take some time. " +
             "Since I want to give you credit for all the steps you do, " +
             "please try to have your watch sync with the phone before our evening checkin. "
             + "Can you try to do that?"),
            options=['Definitely', 'Alright', "I'll try"],
            message_type=Message.Type.MULTIPLE_CHOICE,
            text_populator=text_populator,
        )

    first_meeting = DirectedGraph(
        name='first meeting',
        start_node='ask name',
        nodes=[
            Node(
                name='ask name',
                message=Options.Messages.set_name,
                options='Okay',
                transitions='introduce self',
            ),
            Node(name='introduce self',
                 message=Messages.introduce_self,
                 options=['Nice to meet you', 'Sounds great', 'Okay'],
                 transitions='can explain?'),
            Node(
                name='can explain?',
                content="Can I explain how we'll work together?",
                message_type=Message.Type.MULTIPLE_CHOICE,
                options=['Sure thing', 'Okay', 'No thanks'],
                transitions=[
                    'explain checkins', 'explain checkins', 'set am checkin'
                ],
                text_populator=text_populator,
            ),
            Node(
                name='explain checkins',
                message=Messages.explain_checkin,
                transitions='explain off-checkins',
            ),
            Node(
                name='explain off-checkins',
                message=Messages.explain_off_checkin,
                transitions='explain fitbit',
            ),
            Node(
                name='explain fitbit',
                message=Messages.explain_fitbit,
                transitions='set am checkin',
            ),
            Node(
                name='set am checkin',
                message=Options.Messages.set_am_checkin,
                transitions='confirm am checkin',
            ),
            Node(
                name='confirm am checkin',
                message=Options.Messages.confirm_am_checkin,
                options=[
                    '{affirmative_button_response}', '{oops_button_response}'
                ],
                transitions=['set pm checkin', 'set am checkin'],
            ),
            Node(
                name='set pm checkin',
                message=Options.Messages.set_pm_checkin,
                transitions='confirm pm checkin',
            ),
            Node(
                name='confirm pm checkin',
                message=Options.Messages.confirm_pm_checkin,
                options=[
                    '{affirmative_button_response}', '{oops_button_response}'
                ],
                transitions=['set steps goal', 'set pm checkin'],
            ),
            # THIS NODE IS SKIPPED
            Node(
                name='set day off',
                message=Options.Messages.set_day_off,
                transitions='set steps goal',
            ),
            Node(
                name='confirm day off',
                message=Options.Messages.confirm_day_off,
                options=[
                    '{affirmative_button_response}', '{oops_button_response}'
                ],
                transitions=['set steps goal', 'set day off'],
            ),
            # ---------------------
            Node(
                name='set steps goal',
                content=(
                    lambda:
                    "The last thing to do is to set walking goals for today. "
                    + "You did {'db': '%s'} steps last week. " % state_db.Keys.
                    STEPS_LAST_WEEK +
                    "To work towards the goal of %s steps in %s weeks, " %
                    (param_db.get(param_db.Keys.FINAL_STEPS_GOAL),
                     param_db.get(param_db.Keys.WEEKS_WITH_ROBOT)
                     ) + "I suggest that you do {'db': '%s'} steps today. " %
                    state_db.Keys.SUGGESTED_STEPS_TODAY +
                    "How many steps would you like to do today?"),
                options='steps',
                message_type=Message.Type.SLIDER,
                args=[
                    lambda: "{'db': '%s'}" % state_db.Keys.
                    MIN_SUGGESTED_STEPS_TODAY,
                    lambda: "{'db': '%s'}" % state_db.Keys.
                    MAX_SUGGESTED_STEPS_TODAY,
                    '1',
                    lambda: "{'db': '%s'}" % state_db.Keys.
                    SUGGESTED_STEPS_TODAY,
                ],
                result_convert_from_str_fn=int,
                result_db_key=state_db.Keys.STEPS_GOAL,
                tests=lambda x: x >= state_db.get(state_db.Keys.
                                                  MIN_SUGGESTED_STEPS_TODAY),
                error_message=
                "Please select a goal that is at least {'db': '%s'} steps" %
                state_db.Keys.MIN_SUGGESTED_STEPS_TODAY,
                text_populator=text_populator,
                transitions='set when walk',
            ),
            Node(
                name='set when walk',
                message=Message(
                    content="{when_question}",
                    options='is when',
                    message_type=Message.Type.TIME_ENTRY,
                    args=[
                        '15',
                        lambda: (datetime.datetime.now() + datetime.timedelta(
                            hours=1)).strftime("%H:%M"),
                    ],
                    result_db_key=state_db.Keys.WALK_TIME,
                    result_convert_from_str_fn=lambda x: datetime.datetime.
                    strptime(x, '%I:%M %p').time(),
                    tests=lambda x:
                    (state_db.get(state_db.Keys.AM_CHECKIN_TIME) <= x <=
                     state_db.get(state_db.Keys.PM_CHECKIN_TIME)),
                    error_message=
                    "Please pick a time after now and before our evening checkin",
                    text_populator=text_populator,
                ),
                transitions=['first closing'],
            ),
            Node(name='first closing',
                 content=("*QT/bye*Alright, we're all setup! " +
                          "I'll see you for checkin this evening!"),
                 message_type=Message.Type.MULTIPLE_CHOICE,
                 options=['Bye', 'Talk to you later'],
                 transitions='exit')
        ],
    )