Пример #1
0
 def __init__(self):
     states=['solid', 'liquid', 'gas', 'plasma']
     
     Machine.__init__(self, states=states, initial='solid')
     self.add_transition('melt', 'solid', 'liquid')
     self.add_transition('evaporate', 'liquid', 'gas')
     self.add_transition('sublimate', 'solid', 'gas')
     self.add_transition('ionize', 'gas', 'plasma')
Пример #2
0
	def __init__(self, **machine_configs):
		self.machine = GraphMachine(
			model = self,
			**machine_configs
		)
		self.ref = db.reference()
Пример #3
0
 def __init__(self, name):
     self.name = name
     self.states = ['start']
     self.machine = GraphMachine(model=self, initial='start', title=name, show_conditions=True, show_state_attributes=True)
Пример #4
0
class IR_Model(object):
    def __init__(self, name):
        self.name = name
        self.states = ['start']
        self.machine = GraphMachine(model=self, initial='start', title=name, show_conditions=True, show_state_attributes=True)

    def get_condition_list(self, self_transitions):
        condition_list = []
        existing_conditions = self_transitions[0].conditions
        for condition in existing_conditions:
            condition_list.append(condition.func)  # get the string of the condition, not the condition obj
        return condition_list

    def update_condition_list(self, self_transitions, new_condition_list):
        condition_list = self.get_condition_list(self_transitions)
        for new_condition in new_condition_list:
            if new_condition not in condition_list:
                condition_list.append(new_condition)
        return condition_list

    def add_new_transition(self, trigger, source, dest):
        existing_transitions = self.machine.get_transitions(trigger=trigger, source=source, dest=dest)
        if len(existing_transitions) == 0:
            self.machine.add_transition(trigger=trigger, source=source, dest=dest)
            self.states.append(source)
            self.states.append(dest)

    def add_self_transition(self, state, new_condition_list):
        trigger_list = self.machine.get_triggers(state)
        for trigger in trigger_list:
            if trigger == 'self':  # self loop exists in the usage model
                self_transitions = self.machine.get_transitions('self', state, state)
                condition_list = self.update_condition_list(self_transitions, new_condition_list)
                self.machine.remove_transition('self', state, state)
                self.machine.add_transition(trigger='self', source=state, dest=state, conditions=condition_list)
                self.states.append(state)
                return
        # when self loop doesn't exist or the state doesn't exist
        self.machine.add_transition(trigger='self', source=state, dest=state, conditions=new_condition_list)
        self.states.append(state)
Пример #5
0
class DengueBotMachine(metaclass=Signleton):
    states = list()
    dengue_transitions = list()
    reply_msgs = dict()

    LOCATION_SEND_TUTOIRAL_MSG = [
        ImageSendMessage(original_content_url=loc_step1_origin_img_url,
                         preview_image_url=loc_step1_preview_img_url),
        ImageSendMessage(original_content_url=loc_step2_origin_img_url,
                         preview_image_url=loc_step2_preview_img_url),
    ]

    def __init__(self, line_bot_api, initial_state='user', *, root_path):
        self.config_path_base = root_path if root_path else ''
        self.load_config()
        self.machine = GraphMachine(
            model=self,
            states=DengueBotMachine.states,
            transitions=DengueBotMachine.dengue_transitions,
            initial=initial_state,
            auto_transitions=False,
            show_conditions=True)
        self.line_bot_api = line_bot_api

    def reply_message_with_logging(self, reply_token, receiver_id, messages):
        def save_message(msg):
            try:
                content = msg.text
            except AttributeError:
                content = None

            bot_reply_log = BotReplyLog(
                receiver=LineUser.objects.get(user_id=receiver_id),
                speak_time=datetime.now(),
                message_type=msg.type,
                content=content)
            bot_reply_log.save()

        self.line_bot_api.reply_message(reply_token, messages)

        if not isinstance(messages, (list, tuple)):
            messages = [messages]
        for m in messages:
            save_message(m)

    def draw_graph(self, filename, prog='dot'):
        self.graph.draw(filename, prog=prog)

    def load_config(self, filename='FSM.json'):
        path = os.path.join(self.config_path_base, filename)
        with open(path) as FSM_file:
            data = ujson.loads(jsmin(FSM_file.read()))

        DengueBotMachine.states = data['states']
        DengueBotMachine.dengue_transitions = data['transitions']
        self._load_msg()

    def _load_msg(self, filename='dengue_msg.json'):
        path = os.path.join(self.config_path_base, filename)
        with open(path) as msg_file:
            msgs = ujson.loads(jsmin(msg_file.read()))
            DengueBotMachine.reply_msgs = msgs['reply_msgs']

    def set_state(self, state):
        self.machine.set_state(state)

    def reset_state(self):
        self.set_state('user')

    @log_fsm_condition
    def is_pass(self, event):
        return True

    @log_fsm_condition
    def is_failed(self, event):
        return False

    def _is_this_message_event(self, event, event_type):
        return isinstance(event, MessageEvent) and isinstance(
            event.message, event_type)

    @log_fsm_condition
    def is_text_message(self, event):
        return self._is_this_message_event(event, TextMessage)

    @log_fsm_condition
    def is_sticker_message(self, event):
        return self._is_this_message_event(event, StickerMessage)

    @log_fsm_condition
    def is_image_message(self, event):
        return self._is_this_message_event(event, ImageMessage)

    @log_fsm_condition
    def is_video_message(self, event):
        return self._is_this_message_event(event, VideoMessage)

    @log_fsm_condition
    def is_audio_message(self, event):
        return self._is_this_message_event(event, AudioMessage)

    @log_fsm_condition
    def is_location_message(self, event):
        return self._is_this_message_event(event, LocationMessage)

    @log_fsm_condition
    def is_follow_event(self, event):
        return isinstance(event, FollowEvent)

    @log_fsm_condition
    def is_unfollow_event(self, event):
        return isinstance(event, UnfollowEvent)

    @log_fsm_condition
    def is_join_event(self, event):
        return isinstance(event, JoinEvent)

    @log_fsm_condition
    def is_leave_event(self, event):
        return isinstance(event, LeaveEvent)

    @log_fsm_condition
    def is_postback_event(self, event):
        return isinstance(event, PostbackEvent)

    @log_fsm_condition
    def is_beacon_event(self, event):
        return isinstance(event, BeaconEvent)

    @log_fsm_condition
    def is_asking_prevention(self, event):
        msg = event.message.text
        if (msg.strip() == "如何防範"
                or (any(m in msg
                        for m in ["怎樣", "怎麼", "如何"]) and ("防疫" in msg))
                or any(m in msg for m in ["防範", "預防"])):
            return True
        return False

    @log_fsm_condition
    def is_asking_self_prevention(self, event):
        text = ''
        if self.is_postback_event(event):
            text = event.postback.data
        elif self.is_text_message(event):
            text = event.message.text
        return '自身' in text

    @log_fsm_condition
    def is_asking_env_prevention(self, event):
        text = ''
        if self.is_postback_event(event):
            text = event.postback.data
        elif self.is_text_message(event):
            text = event.message.text
        return '環境' in text

    @log_fsm_condition
    def is_asking_dengue_fever(self, event):
        msg = event.message.text
        if any(m in msg for m in ["什麼是登革熱", "登革熱是什麼"]) or msg.strip() == "登革熱":
            return True
        return False

    @log_fsm_condition
    def is_asking_who_we_are(self, event):
        msg = event.message.text
        if msg.strip() in ["你是誰", "掌蚊人是誰", "誰是掌蚊人", "掌蚊人"]:
            return True
        return False

    @log_fsm_condition
    def is_asking_breeding_source(self, event):
        msg = event.message.text
        if (all(m in msg for m in ["登革熱", "孳生源"])
                or (all(m in msg
                        for m in ["孳生源", "是"]) and any(m in msg
                                                       for m in ["什麼", "啥"]))):
            return True
        return False

    @log_fsm_condition
    def is_greeting(self, event):
        msg = event.message.text
        if any(m in msg.strip().lower()
               for m in ["哈囉", "你好", "嗨", "安安", "hello", "hi"]):
            return True
        return False

    @log_fsm_condition
    def is_asking_hospital(self, event):
        msg = event.message.text
        if (msg.strip() in ["快篩檢驗", "快篩"] or
            (any(m in msg
                 for m in ["最近", "附近", "去哪", "去那", "哪裡", "那裡", "在哪", "在那"])
             and any(m in msg for m in ["快篩", "篩檢", "檢查", "檢驗", "診所", "醫院"]))
                or all(m in msg for m in ["快篩", "診所"])):
            return True
        return False

    @log_fsm_condition
    def is_valid_address(self, event):
        coder = GoogleV3()
        address = event.message.text
        geocode = coder.geocode(address)
        return geocode is not None

    @log_fsm_condition
    def is_asking_symptom(self, event):
        msg = event.message.text
        if (msg.strip() in ["症狀", "登革熱症狀"]
                or all(m in msg for m in ["登革熱", "症狀"])
                or all(m in msg for m in ["症狀", "是"])):
            return True
        return False

    @log_fsm_condition
    def is_asking_realtime_epidemic(self, event):
        msg = event.message.text
        if (msg.strip() in ["即時疫情", "疫情"]
                or (any(m in msg
                        for m in ["最近", "本週", "這週"]) and ("疫情" in msg))
                or ("疫情資訊" in msg)):
            return True
        return False

    @log_fsm_condition
    def is_giving_suggestion(self, event):
        return '建議' in event.message.text

    @log_fsm_condition
    def is_asking_usage(self, event):
        msg = event.message.text
        if (msg.strip() in ["聊什麼", "翻譯蒟蒻"] or any(m in msg
                                                  for m in ["使用說明", "功能"]) or
            (any(m in msg for m in ["可以", "能", "會"])
             and any(m in msg
                     for m in ["做啥", "幹麻", "幹嘛", "幹啥", "什麼", "幹什麼", "做什麼"]))
                or all(m in msg for m in ["有", "功能"])
                or ("怎麼" in msg and any(m in msg for m in ["使用", "用"]))):
            return True
        return False

    @log_fsm_condition
    def is_selecting_ask_dengue_fever(self, event):
        return '1' == event.message.text or self.is_asking_dengue_fever(event)

    @log_fsm_condition
    def is_selecting_ask_symptom(self, event):
        return '2' == event.message.text or self.is_asking_symptom(event)

    @log_fsm_condition
    def is_selecting_ask_prevention(self, event):
        return '3' == event.message.text or self.is_asking_prevention(event)

    @log_fsm_condition
    def is_selecting_ask_hospital(self, event):
        return '4' == event.message.text or self.is_asking_hospital(event)

    @log_fsm_condition
    def is_selecting_ask_realtime_epidemic(self, event):
        return '5' == event.message.text or self.is_asking_realtime_epidemic(
            event)

    @log_fsm_condition
    def is_selecting_give_suggestion(self, event):
        return '6' == event.message.text or self.is_giving_suggestion(event)

    @log_fsm_condition
    def is_hospital_address(self, event):
        return 'hosptial_address' in parse_qs(event.postback.data)

    @log_fsm_operation
    def is_gov_report(self, event):
        return '#2016' in event.message.text

    @log_fsm_operation
    def on_enter_user_join(self, event):
        # TODO: implement update user data when user rejoin
        user_id = event.source.user_id
        try:
            LineUser.objects.get(user_id=user_id)
        except LineUser.DoesNotExist:
            profile = self.line_bot_api.get_profile(user_id)
            user = LineUser(user_id=profile.user_id,
                            name=profile.display_name,
                            picture_url=profile.picture_url or '',
                            status_message=profile.status_message or '')
            user.save()
        self.finish()

    def _send_text_in_rule(self, event, key):
        self.reply_message_with_logging(
            event.reply_token, event.source.user_id,
            TextSendMessage(text=DengueBotMachine.reply_msgs[key]))

    @log_fsm_operation
    def on_enter_ask_prevention(self, event):
        self.reply_message_with_logging(
            event.reply_token, event.source.user_id,
            TemplateSendMessage(
                alt_text=DengueBotMachine.reply_msgs['ask_prevent_type'],
                template=ButtonsTemplate(text='請問是自身防疫還是環境防疫呢?',
                                         actions=[
                                             PostbackTemplateAction(label='自身',
                                                                    data='自身'),
                                             PostbackTemplateAction(label='環境',
                                                                    data='環境'),
                                         ])))

    @log_fsm_operation
    def on_enter_ask_self_prevention(self, event):
        self._send_text_in_rule(event, 'self_prevent')
        self.finish_ans()

    @log_fsm_operation
    def on_enter_ask_env_prevention(self, event):
        self._send_text_in_rule(event, 'env_prevent')
        self.finish_ans()

    @log_fsm_operation
    def on_enter_ask_dengue_fever(self, event):
        self._send_text_in_rule(event, 'dengue_fever_intro')
        self.finish_ans()

    @log_fsm_operation
    def on_enter_ask_hospital(self, event):
        messages = [
            TextSendMessage(text=DengueBotMachine.reply_msgs['ask_address'])
        ]
        messages.extend(self.LOCATION_SEND_TUTOIRAL_MSG)
        self.reply_message_with_logging(event.reply_token,
                                        event.source.user_id,
                                        messages=messages)
        self.advance()

    @log_fsm_operation
    def on_enter_receive_user_location(self, event):
        hospital_list = hospital.views.get_nearby_hospital(
            event.message.longitude, event.message.latitude)
        self._send_hospital_msgs(hospital_list, event)
        self.finish_ans()

    @log_fsm_condition
    def on_enter_receive_user_address(self, event):
        coder = GoogleV3()
        address = event.message.text
        geocode = coder.geocode(address)
        hospital_list = hospital.views.get_nearby_hospital(
            geocode.longitude, geocode.latitude)
        self._send_hospital_msgs(hospital_list, event)
        self.finish_ans()

    def _send_hospital_msgs(self, hospital_list, event):
        if hospital_list:
            msgs = self._create_hospitals_msgs(hospital_list)
        else:
            msgs = [TextSendMessage(text="抱歉,你附近都沒有快篩診所\n")]
            msgs.append(
                TextSendMessage(text=(
                    "想要查看地區所有快篩點,請點下面連結\n"
                    "(如果手機不能瀏覽,可用電腦查看,或將連結貼到 chrome 瀏覽器)\n\n"
                    "https://www.taiwanstat.com/realtime/dengue-vis-with-hospital/"
                )))
        self.reply_message_with_logging(event.reply_token,
                                        event.source.user_id, msgs)

    def _create_hospitals_msgs(self, hospital_list):
        text = "您好,\n最近的三間快篩診所是:"
        carousel_messages = list()
        for index, hospital in enumerate(hospital_list, 1):
            name = hospital.get('name')
            address = hospital.get('address')
            phone = hospital.get('phone')

            text += "\n\n{index}.{name}\n{address}\n{phone}".format(
                index=index, name=name, address=address, phone=phone)

            carousel_messages.append(
                CarouselColumn(text=name,
                               actions=[
                                   PostbackTemplateAction(
                                       label=address[:20],
                                       text=' ',
                                       data='hosptial_address=' + address,
                                   ),
                                   MessageTemplateAction(label=phone[:20],
                                                         text=' '),
                               ]))

        carousel_messages.append(
            CarouselColumn(
                text='想要查看地區所有快篩點\n請點下面連結',
                actions=[
                    MessageTemplateAction(label=' ', text=' '),
                    URITemplateAction(
                        label='鄰近快篩診所',
                        uri=
                        'https://www.taiwanstat.com/realtime/dengue-vis-with-hospital/',
                    )
                ]))

        template_message = TemplateSendMessage(
            alt_text=text,
            template=CarouselTemplate(columns=carousel_messages))

        hospital_messages = [template_message]
        return hospital_messages

    @log_fsm_operation
    def on_enter_ask_symptom(self, event):
        self.reply_message_with_logging(
            event.reply_token,
            event.source.user_id,
            messages=[
                ImageSendMessage(original_content_url=symptom_origin_img_url,
                                 preview_image_url=symptom_preview_img_url),
                TextSendMessage(
                    text=DengueBotMachine.reply_msgs['symptom_warning'])
            ])
        self.finish_ans()

    @log_fsm_operation
    def on_enter_ask_realtime_epidemic(self, event):
        self._send_text_in_rule(event, 'new_condition')
        self.finish_ans()

    @log_fsm_operation
    def on_enter_greet(self, event):
        self._send_text_in_rule(event, 'greeting')
        self.finish_ans()

    @log_fsm_operation
    def on_enter_ask_breeding_source(self, event):
        self._send_text_in_rule(event, 'breeding_source')

    @log_fsm_operation
    def on_enter_ask_who_we_are(self, event):
        self._send_text_in_rule(event, 'who_we_are')
        self.finish_ans()

    @log_fsm_operation
    def on_enter_wait_user_suggestion(self, event):
        self._send_text_in_rule(event, 'ask_advice')

    @log_fsm_operation
    def on_exit_wait_user_suggestion(self, event):
        self._send_text_in_rule(event, 'thank_advice')
        advice = Advice(content=event.message.text,
                        user_id=event.source.user_id)
        advice.save()

    @log_fsm_operation
    def on_enter_ask_usage(self, event):
        self._send_text_in_rule(event, 'manual')

    @log_fsm_operation
    def on_enter_gov_faculty_report(self, event):
        text = event.message.text
        _, _, action, note = text.split('#')

        gov_report = GovReport(
            user_id=LineUser.objects.get(user_id=event.source.user_id),
            action=action,
            note=note,
            report_time=datetime.fromtimestamp(event.timestamp / 1000),
        )
        gov_report.save()
        self.advance(event)

    @log_fsm_operation
    def on_enter_wait_gov_location(self, event):
        messages = [
            TextSendMessage(
                text=DengueBotMachine.reply_msgs['ask_gov_address'])
        ]
        messages.extend(self.LOCATION_SEND_TUTOIRAL_MSG)
        self.reply_message_with_logging(event.reply_token,
                                        event.source.user_id,
                                        messages=messages)

    @log_fsm_operation
    def on_enter_receive_gov_location(self, event):
        try:
            gov_report = GovReport.objects.filter(
                user_id=event.source.user_id, ).order_by('-report_time')[0]
        except GovReport.DoesNotExist:
            logger.error('Gov Report Does Not Exist')
        else:
            gov_report.lat = event.message.latitude
            gov_report.lng = event.message.longitude
            gov_report.save()

            self.reply_message_with_logging(
                event.reply_token,
                event.source.user_id,
                messages=TextSendMessage(
                    text=DengueBotMachine.reply_msgs['thank_gov_report']))
        self.finish_ans()

    @log_fsm_operation
    def on_enter_unrecognized_msg(self, event):
        if getattr(event, 'reply_token', None):
            msg_log = MessageLog.objects.get(speaker=event.source.user_id,
                                             speak_time=datetime.fromtimestamp(
                                                 event.timestamp / 1000),
                                             content=event.message.text)
            unrecognized_msg = UnrecognizedMsg(message_log=msg_log)
            unrecognized_msg.save()

            try:
                response_to_unrecog_msg = ResponseToUnrecogMsg.objects.get(
                    unrecognized_msg_content=unrecognized_msg.message_log.
                    content)
            except ResponseToUnrecogMsg.DoesNotExist:
                self._send_text_in_rule(event, 'unknown_msg')
            else:
                response_content = response_to_unrecog_msg.content
                self.line_bot_api.reply_message(
                    event.reply_token, TextSendMessage(text=response_content))
        self.handle_unrecognized_msg(event)

    @log_fsm_operation
    def on_enter_ask_hospital_map(self, event):
        address = parse_qs(event.postback.data)['hosptial_address'][0]
        hosp = hospital.models.Hospital.objects.using('tainan').get(
            address=address)
        self.line_bot_api.reply_message(
            event.reply_token,
            messages=LocationSendMessage(
                title="地圖 - {name}".format(name=hosp.name),
                address=hosp.address,
                latitude=hosp.lat,
                longitude=hosp.lng))
        self.finish_ans()
Пример #6
0
class TocMachine(GraphMachine):
    def __init__(self, **machine_configs):
        self.machine = GraphMachine(model=self, **machine_configs)
        self.user = User()
        self.bus_info = BusInfo()
        self.train_info = TrainInfo()
        self.hsr_info = HsrInfo()
        self.prev = None

    def reset(self):
        self.machine.set_state(self.machine.initial)
        self.on_enter_user()

    def on_enter_user(self, event=None):
        if self.user.user_id != None:
            push_text_message(self.user.user_id, '歡迎使用時刻表查詢系統,輸入任意文字開始')

    def is_going_to_start(self, event=None):
        self.user.user_id = event.source.user_id
        return True

    def on_enter_start(self, event=None):
        contents = get_replay_flex('start')
        send_flex_message(event.reply_token, contents)

    def is_going_to_bus_start(self, event=None):
        if event.message.type == 'text' and event.message.text == '公車查詢':
            return True
        else:
            if not (event.message.type == 'text' and
                    (event.message.text == 'reset' or event.message.text
                     == 'bus' or event.message.text == '火車查詢'
                     or event.message.text == '高鐵查詢')):
                push_text_message(self.user.user_id, '無法辨識...')
            return False

    def on_enter_bus_start(self, event=None):
        self.user.bus_stops = None
        self.user.bus_route = None
        self.user.bus_direction = None
        contents = get_replay_flex('bus')
        send_flex_message(event.reply_token, contents)

    def going_bus_by_stop(self, event=None):
        if event.message.type != 'text':
            return False
        if event.message.text == '以站牌搜尋':
            return True
        else:
            # if event.message.type != 'location' and not (event.message.type == 'text' and (event.message.text == 'reset' or event.message.text == '以路線搜尋')):
            if not (event.message.text == 'reset'
                    or event.message.text == '以路線搜尋'):
                push_text_message(self.user.user_id, '請點選上方的選項\n如要重來')
            return False

    def on_enter_bus_by_stop(self, event=None):
        send_text_message(event.reply_token, "請輸入站牌名稱")

    def going_bus_by_stop2(self, event=None):
        if event.message.type == 'text':
            if event.message.text not in self.bus_info.stops_to_route:
                push_text_message(
                    self.user.user_id,
                    f'找不到 {event.message.text} 站牌,目前只支援台南地區的公車站')
                return False
            else:
                self.user.bus_stops = event.message.text
                return True
        else:
            push_text_message(self.user.user_id, '請輸入公車站名字')
            return False

    def on_enter_bus_by_stop2(self, event=None):
        contents = get_replay_flex('bus_route_by_stops')  # input
        routes_list = natsorted(
            self.bus_info.stops_to_route[self.user.bus_stops])
        contents['body']['contents'][0]['text'] = self.user.bus_stops
        for route in routes_list:
            template = copy.deepcopy(button_template)
            template['action']['label'] = template['action']['text'] = route
            contents['footer']['contents'].append(template)
        send_flex_message(event.reply_token, contents)

    def goint_bus_by_route(self, event=None):
        return event.message.type == 'text' and event.message.text == '以路線搜尋'

    def on_enter_bus_by_route(self, event=None):
        send_text_message(event.reply_token, "請輸入公車號碼")

    def goint_bus_by_route2(self, event=None):
        # get shop from number
        if event.message.type == 'text':
            if event.message.text not in self.bus_info.route_to_stops:
                push_text_message(
                    self.user.user_id,
                    f'找不到公車路線為 {event.message.text} 的公車,目前只支援台南地區的公車')
                return False
            else:
                self.user.bus_route = event.message.text
                return True
        else:
            push_text_message(self.user.user_id, '請輸入公車路線')
            return False

    def on_enter_bus_by_route2(self, event=None):
        contents = get_replay_flex('bus_stops_by_route')  # input
        stops_list = self.bus_info.route_to_stops[self.user.bus_route]
        template = copy.deepcopy(bubble_template)
        template['body']['contents'][0]['text'] = self.user.bus_route
        for i, stop in enumerate(stops_list):
            if i >= 72:  # line can not sent message which exceed 12 bubble
                break
            if i % 6 == 0:
                contents['contents'].append(copy.deepcopy(template))
            dict_template = copy.deepcopy(button_template)
            dict_template['action']['label'] = dict_template['action'][
                'text'] = stop
            contents['contents'][-1]['footer']['contents'].append(
                dict_template)
        send_flex_message(event.reply_token, contents)

    # need google map api
    # def goint_bus_by_location(self, event = None):
    #    # get shop from location
    #    if event.message.type == 'location':
    #        return True
    #    else:
    #        return False

    # def on_enter_bus_by_location(self, event = None):
    #    contents = get_replay_flex('stops_by_location') # 附近的公車站
    #    push_flex_message(self.user.user_id, contents)

    def going_bus_direction(self, event=None):
        # print result
        if self.user.bus_route == None:
            if event.message.type == 'text':
                if event.message.text in self.bus_info.route_to_stops:
                    self.user.bus_route = event.message.text
                    contents = get_replay_flex('bus_direction')
                    push_flex_message(self.user.user_id, contents)
                    return True
                else:
                    push_text_message(self.user.user_id, '請點選上方的路線')
                    return False
            else:
                push_text_message(self.user.user_id, '請點選上方的路線')
                return False
        else:
            if event.message.type == 'text':
                if event.message.text in self.bus_info.stops_to_route:
                    self.user.bus_stops = event.message.text
                    contents = get_replay_flex('bus_direction')
                    push_flex_message(self.user.user_id, contents)
                    return True
                else:
                    push_text_message(self.user.user_id, '請點選上方的站牌名稱')
                    return False
            else:
                push_text_message(self.user.user_id, '請點選上方的站牌名稱')
                return False

    def is_going_to_bus_end(self, event=None):
        if event.message.type == 'text':
            if event.message.text in ['去程', '回程']:
                self.user.bus_direction = event.message.text
                estimates = self.bus_info.calculate(self.user.bus_route,
                                                    self.user.bus_stops,
                                                    self.user.bus_direction)
                contents = get_replay_flex('show_bus_time')
                contents['header']['contents'][0]['text'] = self.user.bus_route
                contents['header']['contents'][1][
                    'text'] = self.user.bus_direction
                i = 1
                # contents['body']['contents'][0]['contents'][1]['contents'][1]['borderColor'] = '#00FF00'
                # contents['body']['contents'][0]['contents'][1]['contents'][1]['backgroundColor'] = '#00FF00'
                real_time = 0
                for key, value in estimates.items():
                    if i == 3:
                        real_time = value[0]
                    contents['body']['contents'][i]['contents'][2][
                        'text'] = key
                    if value[1] == 0:
                        # 末班車已過 or 交管
                        contents['body']['contents'][i]['contents'][0][
                            'text'] = f'{(timedelta(seconds =  value[0])).seconds // 60} 分' if value[
                                0] != None else '不停靠'
                    elif value[1] == 1:
                        contents['body']['contents'][i]['contents'][0][
                            'text'] = f'{(timedelta(seconds =  value[0])).seconds // 60} 分' if value[
                                0] != None else '未發車'
                    elif value[1] == 2:
                        contents['body']['contents'][i]['contents'][0][
                            'text'] = '交管不停靠'
                    elif value[1] == 3:
                        contents['body']['contents'][i]['contents'][0][
                            'text'] = '末班車已過'
                    elif value[1] == 4:
                        contents['body']['contents'][i]['contents'][0][
                            'text'] = '今日未營運'
                    else:
                        # 末班車已過 or 交管
                        contents['body']['contents'][i]['contents'][0][
                            'text'] = '不停靠'
                    i += 2
                contents['header']['contents'][1][
                    'text'] = self.user.bus_direction
                contents['body']['contents'][0][
                    'text'] = f'預計到達時間: {int(real_time / 60)} 分鐘' if real_time != None else ' '
                send_flex_message(event.reply_token, contents)
                return True
            else:
                push_text_message(self.user.user_id, '請輸入正確的方向')
                return False
        else:
            push_text_message(self.user.user_id, '請輸入正確的方向')
            return False

    def on_enter_bus_end(self, event=None):
        self.machine.set_state(self.machine.initial)

    def is_going_to_train(self, event=None):
        return event.message.type == 'text' and event.message.text == '火車查詢'

    def on_enter_train_start(self, event=None):
        self.train_info.start_station = None
        self.train_info.end_station = None
        contents = get_replay_flex('train_start')
        send_flex_message(event.reply_token, contents)

    def going_train_start_station(self, event=None):
        if event.message.type == 'text' and event.message.text in self.train_info.available_station:
            self.user.train_start_station = event.message.text
            return True
        else:
            push_text_message(self.user.user_id, '請選擇上方的車站')
            return False

    def on_enter_train_start_station(self, event=None):
        contents = get_replay_flex('train_end')
        send_flex_message(event.reply_token, contents)

    def going_train_end_station(self, event=None):
        if event.message.type == 'text' and event.message.text in self.train_info.available_station:
            if event.message.text == self.user.train_start_station:
                push_text_message(self.user.user_id, "起訖站不可相同")
                return False
            else:
                self.user.train_end_station = event.message.text
                return True
        else:
            push_text_message(self.user.user_id, '請選擇上方的車站')
            return False

    def on_enter_train_end_station(self, event=None):
        contents = get_replay_flex('train_date')
        contents['header']['contents'][0]['contents'][1][
            'text'] = self.user.train_start_station
        contents['header']['contents'][1]['contents'][1][
            'text'] = self.user.train_end_station
        send_flex_message(event.reply_token, contents)

    def going_train_time(self, event=None):
        if event.type == 'postback':
            self.user.train_date = event.postback.params['datetime']
            times = self.train_info.calculate(self.user.train_start_station,
                                              self.user.train_end_station,
                                              self.user.train_date)
            contents = get_replay_flex('show_train_time')

            for t in times:
                copy_template = copy.deepcopy(train_time_template)
                copy_template['header']['contents'][0]['contents'][0][
                    'contents'][1]['text'] = self.user.train_start_station
                copy_template['header']['contents'][0]['contents'][1][
                    'contents'][1]['text'] = self.user.train_end_station
                copy_template['header']['contents'][1]['contents'][0][
                    'text'] = f'車號 {t[2]}'
                # too long
                copy_template['header']['contents'][1]['contents'][1][
                    'text'] = f'{t[3][:2]}'
                delay = self.train_info.delay(t[2])
                if delay != 0:
                    copy_template['body']['contents'][0]['contents'][1][
                        'contents'][1]['borderColor'] = '#EF4520'
                    copy_template['body']['contents'][0]['contents'][1][
                        'contents'][1]['backgroundColor'] = '#EF4520'
                    copy_template['body']['contents'][0]['contents'][2][
                        'text'] = f'誤點 {int(delay / 60)} 分鐘'
                else:
                    copy_template['body']['contents'][0]['contents'][2][
                        'text'] = f'正常'
                td = datetime.strptime(t[1], "%H:%M") - \
                    datetime.strptime(t[0], "%H:%M")
                copy_template['body']['contents'][2][
                    'text'] = f'Total: {td.seconds // 3600} hours {(td.seconds // 60) % 60} minutes'
                copy_template['body']['contents'][3]['contents'][0][
                    'text'] = t[0]
                copy_template['body']['contents'][3]['contents'][2][
                    'text'] = self.user.train_start_station
                copy_template['body']['contents'][5]['contents'][0][
                    'text'] = t[1]
                copy_template['body']['contents'][5]['contents'][2][
                    'text'] = self.user.train_end_station

                contents['contents'].append(copy_template)

            if len(times) == 0:
                send_text_message(event.reply_token,
                                  f"{self.user.train_date} 之後沒有可以搭乘的列車")
            else:
                send_flex_message(event.reply_token, contents)
            return True
        else:
            push_text_message(self.user.user_id, '請輸入正確的日期')
            return False

    def on_enter_train_time(self, event=None):
        self.machine.set_state(self.machine.initial)

    def is_going_to_hsr(self, event=None):
        return event.message.type == 'text' and event.message.text == '高鐵查詢'

    def on_enter_hsr_start(self, event=None):
        self.train_info.start_station = None
        self.train_info.end_station = None
        contents = get_replay_flex('hsr_start')
        send_flex_message(event.reply_token, contents)

    def going_hsr_start_station(self, event=None):
        if event.message.type == 'text' and event.message.text in self.hsr_info.available_station:
            self.user.hsr_start_station = event.message.text
            return True
        else:
            push_text_message(self.user.user_id, '請選擇上方的車站')
            return False

    def on_enter_hsr_start_station(self, event=None):
        contents = get_replay_flex('hsr_end')
        send_flex_message(event.reply_token, contents)

    def going_hsr_end_station(self, event=None):
        if event.message.type == 'text' and event.message.text in self.hsr_info.available_station:
            if event.message.text == self.user.hsr_start_station:
                push_text_message(self.user.user_id, "起訖站不可相同")
                return False
            else:
                self.user.hsr_end_station = event.message.text
                return True
        else:
            push_text_message(self.user.user_id, '請選擇上方的車站')
            return False

    def on_enter_hsr_end_station(self, event=None):
        contents = get_replay_flex('train_date')
        contents['header']['contents'][0]['contents'][1][
            'text'] = self.user.hsr_start_station
        contents['header']['contents'][1]['contents'][1][
            'text'] = self.user.hsr_end_station
        send_flex_message(event.reply_token, contents)

    def going_hsr_time(self, event=None):
        if event.type == 'postback':
            self.user.hsr_date = event.postback.params['datetime']
            times = self.hsr_info.calculate(self.user.hsr_start_station,
                                            self.user.hsr_end_station,
                                            self.user.hsr_date)
            contents = get_replay_flex('show_hsr_time')

            for t in times:
                copy_template = copy.deepcopy(rail_time_template)
                copy_template['header']['contents'][0]['contents'][0][
                    'contents'][1]['text'] = self.user.hsr_start_station
                copy_template['header']['contents'][0]['contents'][1][
                    'contents'][1]['text'] = self.user.hsr_end_station
                copy_template['header']['contents'][1]['contents'][0][
                    'text'] = f'{t[2]}'
                available = self.hsr_info.available_seats(
                    t[2], t[3], self.user.hsr_date,
                    self.user.hsr_start_station)
                if not available:
                    copy_template['body']['contents'][0]['contents'][1][
                        'contents'][1]['borderColor'] = '#EF4520'
                    copy_template['body']['contents'][0]['contents'][1][
                        'contents'][1]['backgroundColor'] = '#EF4520'
                    copy_template['body']['contents'][0]['contents'][2][
                        'text'] = '沒有空位'
                td = datetime.strptime(t[1], "%H:%M") - \
                    datetime.strptime(t[0], "%H:%M")
                copy_template['body']['contents'][2][
                    'text'] = f'Total: {td.seconds // 3600} hours {(td.seconds // 60) % 60} minutes'
                copy_template['body']['contents'][3]['contents'][0][
                    'text'] = t[0]
                copy_template['body']['contents'][3]['contents'][2][
                    'text'] = self.user.hsr_start_station
                copy_template['body']['contents'][5]['contents'][0][
                    'text'] = t[1]
                copy_template['body']['contents'][5]['contents'][2][
                    'text'] = self.user.hsr_end_station
                contents['contents'].append(copy_template)

            if len(times) == 0:
                send_text_message(event.reply_token,
                                  f'{self.user.hsr_date} 之後沒有可以搭乘的列車')
            else:
                send_flex_message(event.reply_token, contents)
            return True
        else:
            push_text_message(self.user.user_id, '請輸入正確的日期')
            return False

    def on_enter_hsr_time(self, event=None):
        self.machine.set_state(self.machine.initial)
Пример #7
0
class ungetauftes_wesen(object):
  
    states = ['idle', 'move', 'check', 'move_straight', 'move_to_side','move_forward', 'move_back', 'orientate', 'open_gripper', 'rotate_gripper_fixed','vision','rotate_gripper_free','close_gripper','check_grip']

    def __init__(self, name):

        self.name = name
        
        self.havecommand = False
        self.command = " "
        
        self.angle_of_deviation = 0.0
        
        self.forward = True
        
        self.gripA = True
        self.gripB = True

        self.orientation = [0,0,0,0,0]
        self.moves_left=5
               

        # Initialize the state machine
        self.machine = Machine(model=self, states=ungetauftes_wesen.states, initial='idle')
        
       

        self.machine.add_transition(trigger='wake_up', source='idle', dest='move')
      
        self.machine.add_transition('move_straight', 'move', 'move_forward',conditions=['is_direction_forward'])
        self.machine.add_transition('move_straight', 'move', 'move_back',unless=['is_direction_forward'])
        
        self.machine.add_transition('orientate', 'move_forward', 'orientate')
        self.machine.add_transition('orientate', 'move_back', 'orientate')
        
        self.machine.add_transition('open_gripper', 'orientate', 'open_gripper')
        self.machine.add_transition('close_gripper', 'vision', 'close_gripper')
         
        self.machine.add_transition('rotate_gripper_fixed', 'open_gripper', 'rotate_gripper_fixed')
        
        self.machine.add_transition('vision', 'rotate_gripper_fixed', 'vision')
                
           
        self.machine.add_transition('check', 'idle', 'check')

        # Sweat is a disorder that can be remedied with water.
        # Unless you've had a particularly long day, in which case... bed time!
        self.machine.add_transition('move_finished', 'close_gripper', 'idle', conditions=['is_exhausted'])
        self.machine.add_transition('move_finished', 'close_gripper', 'move',)
        
      
        #self.machine.add_ordered_transitions(['move_forward', 'orientate'])
  
## CONDITIONS  

    def is_exhausted(self):
        """ Basically a coin toss. """
        return self.moves_left < 0.5
        
    def is_direction_forward(self): return self.forward
    
## CONDITIONS 
           
        
    def on_enter_move(self):
        print("entered state move")
        self.moves_left-=1
        print str(self.moves_left)+(" moves left")
        self.move_straight()
               
    def on_enter_move_forward(self):
        print("entered state move forward flag set")
        self.forward = False
        self.orientate()
    def on_enter_move_back(self):
        print("entered state move back flag set")
        self.forward = True
        self.orientate()
        
        
    def on_enter_idle(self):
        print("entered state idle")
        
    def on_enter_orientate(self):
        print("entered state orientate")   
        self.open_gripper()
        
    def on_enter_open_gripper(self):
        print("entered state open_gripper")
        senddata("testdata")
        print str(checkdata('fever'))
        self.rotate_gripper_fixed()
        
    def on_enter_rotate_gripper_fixed(self):
        print("entered state rotate_gripper_fixed")
        print("Wait for Signal:")
        self.havecommand = True
        self.command = "rotate"
        
    def on_enter_vision(self):
        print("entered state vision")
        
        self.angle_of_deviation = get_course()
        
        print "Our course is: " + str(self.angle_of_deviation)
        
        self.close_gripper()
        
        
                    
                
    def on_enter_close_gripper(self):
        print("entered state close_gripper")
        print("Wait for Signal:")
        sleep(.5)
        print(".")
        sleep(.5)
        print(".")
        sleep(.5)
        print(".")
        
        self.move_finished()
Пример #8
0
class Gandalf(object):
    face_in_front = False
    wants_to_enter = None
    testing_mode = False
    allowed_people_dict = {}
    current_person = None
    delete_faces = True
    force_make_queue = False

    STATES = [
        "start", "started", "initialized", "face_detected",
        "intention_recognized", "validate_card", "on_side"
    ]

    def __init__(self, robot, testing_mode=False):
        self.robot = robot
        self.testing_mode = testing_mode

        # Initialize the state machine
        self.state_machine = Machine(model=self,
                                     states=self.STATES,
                                     queued=True,
                                     initial="start")

        self.state_machine.add_transition(
            trigger="starting_up",
            source="start",
            dest="started",
            after=lambda *args, **kwargs: starting_up(self, *args, **kwargs))
        self.state_machine.add_transition(
            trigger="initializing",
            source="started",
            dest="initialized",
            after=lambda *args, **kwargs: initializing(self, *args, **kwargs))
        self.state_machine.add_transition(
            trigger="detecting_face",
            source="*",
            dest="face_detected",
            after=lambda *args, **kwargs: recognizing_face(
                self, *args, **kwargs))
        self.state_machine.add_transition(
            trigger="question_intention",
            source="face_detected",
            dest="intention_recognized",
            after=lambda *args, **kwargs: intention_recognizing(
                self, *args, **kwargs))

        self.state_machine.add_transition(
            trigger="validate_entry",
            source=["intention_recognized", "validate_card"],
            dest="validate_card",
            after=lambda *args, **kwargs: validation_card_reading(
                self, *args, **kwargs))

        self.state_machine.add_transition(
            trigger="move_to_side",
            source="validate_card",
            dest="on_side",
            after=lambda *args, **kwargs: move_to_side(self, *args, **kwargs))
        self.state_machine.add_transition(
            trigger="move_back",
            source="on_side",
            dest="initialized",
            after=lambda *args, **kwargs: move_back(self, *args, **kwargs))

        self.state_machine.add_transition(
            trigger="re_initialize",
            source="*",
            dest="initialized",
            after=lambda *args, **kwargs: initializing(self, *args, **kwargs))
Пример #9
0
    # Accepted in <num>
    {
        'trigger': 'Input numbers',
        'source': states[5],
        'dest': states[5]
    },
    {
        'trigger': 'Input others',
        'source': states[5],
        'dest': states[0]
    },

    # Accepted in <any>
    {
        'trigger': 'Input',
        'source': states[6],
        'dest': states[6]
    }
]

model = Model()
machine = GraphMachine(model=model,
                       states=states,
                       transitions=transitions,
                       initial=states[0],
                       title='LoggerParser State Transition Diagram')

graph = model.get_graph()
graph.draw('logger_parser_state_transition_diagram.png', prog='dot')
Пример #10
0
 def __init__(self, **machine_configs):
     self.machine = GraphMachine(model=self, **machine_configs)
     self.p = password()
     self.g = GuessA4B4()
Пример #11
0
class FsmGps(object):
    """ FSM states
    """
    states = ['init', 'OFF', 'ON', 'ASSAULT']
    """ Default timeout for GPS frame sending
    """
    def __init__(self, name, mi_sim868):
        self.TIMEOUT_DEFAULT = 30
        self.name = name
        """ Input shared variables 
        """
        self.flag_active = 0
        """ Timeout GPS
        """
        self.timeout_gps = 0
        ''' GPS dictionary
        '''
        self.gps_param = {
            'GNSS_status': 0,
            'UTC': '',  # yyyyMMddhhmmss.sss
            'Latitude': 0.0,  # ±dd.dddddd            [-90.000000,90.000000]
            'Longitude': 0.0,  # ±ddd.dddddd           [-180.000000,180.000000]
            'Altitude': 0.0,  # in meters
            'Speed': 0.0,  # km/h [0,999.99]
        }
        """ Initialize the state machine
        """
        self.machine = Machine(model=self,
                               states=FsmGps.states,
                               initial='init')
        self.sim868 = mi_sim868
        """ Add transitions
        """
        self.machine.add_transition(trigger='start', source='init', dest='OFF')
        self.machine.add_transition('fire',
                                    'OFF',
                                    'ON',
                                    conditions=['is_active'])
        self.machine.add_transition('fire',
                                    'ON',
                                    'OFF',
                                    conditions=['is_not_active'])
        self.machine.add_transition('fire',
                                    'ON',
                                    'ON',
                                    conditions=['timeout_and_active'],
                                    after='send_gps_frame_and_init_timer')

    """ Guard functions
    """

    def is_active(self):
        return self.flag_active

    def is_not_active(self):
        return not self.flag_active

    def timeout_and_active(self):
        return ((int(dt.now().timestamp()) > self.timeout_gps)
                and self.flag_active)

    """ Output functions
    TODO: Modify GPS frame sending with GPS drivers
    """

    def send_gps_frame_and_init_timer(self):
        gps_data = self.sim868.get_gps_data()
        self.gps_param['UTC'] = gps_data["UTC"]
        self.gps_param['Latitude'] = gps_data["Latitude"]
        self.gps_param['Longitude'] = gps_data["Longitude"]
        self.gps_param['Altitude'] = gps_data["Altitude"]
        self.gps_param['Speed'] = gps_data["Speed"]

        print(
            "'UTC': {}, 'Latitude': {}, 'Longitude': {}, 'Altitude': {}, 'Speed': {}"
            .format(self.gps_param["UTC"], self.gps_param["Latitude"],
                    self.gps_param["Longitude"], self.gps_param["Altitude"],
                    self.gps_param["Speed"]))
        self.timeout_gps = int(dt.now().timestamp()) + self.TIMEOUT_DEFAULT
    def __init__(
        self,
        name='myobservatory',
        OTA='OTA',
        states_file='states.yaml',
        transitions_file='transitions.yaml',
        location_file='location.yaml',
        initial_state='sleeping',
        waittime=2,
        maxwait=10,
        max_allowed_errors=0,
        weather=None,
        weather_config={},
        roof=None,
        roof_config={},
        telescope=None,
        telescope_config={},
        instrument=None,
        instrument_config={},
        detector=None,
        detector_config=[{}],
        datadir='~',
        lat=0,
        lon=0,
        height=0,
        horizon=0,
        mongoIP='192.168.4.49',
        mongoport=32768,
        loglevel_console='INFO',
        logfile=None,
        loglevel_file='DEBUG',
        OBs=[],
    ):
        self.name = name
        self.datadir = Path(datadir).expanduser().absolute()
        self.logger = create_log(loglevel_console=loglevel_console,
                                 logfile=logfile,
                                 loglevel_file=loglevel_file)
        self.uname_result = os.uname()
        # Components
        self.weather = weather(logger=self.logger, **weather_config)
        self.roof = roof(logger=self.logger, **roof_config)
        self.telescope = telescope(logger=self.logger, **telescope_config)
        self.instrument = instrument(logger=self.logger, **instrument_config)
        self.detector = [
            d(logger=self.logger, **detector_config[i])
            for i, d in enumerate(detector)
        ]
        self.scheduler = Scheduler(OBs=OBs)

        # Load States File
        with open(Path(states_file).expanduser()) as FO:
            self.states = yaml.safe_load(FO)
        # Load Transitions File
        with open(Path(transitions_file).expanduser()) as FO:
            self.transitions = yaml.safe_load(FO)
        # Load Location
        self.location = c.EarthLocation(lat=lat, lon=lon, height=height)
        self.horizon = horizon
        # Instantiate State Machine
        try:
            self.machine = GraphMachine(
                model=self,
                states=self.states,
                transitions=self.transitions,
                initial=initial_state,
                queued=True,
                use_pygraphviz=True,
                show_conditions=True,
            )
            # Generate state diagram


#             self.machine.get_graph().draw('state_diagram.png', prog='dot')
        except:
            self.machine = Machine(
                model=self,
                states=self.states,
                transitions=self.transitions,
                initial=initial_state,
                queued=True,
            )
        # Operational Properties
        self.waittime = waittime
        self.maxwait = maxwait
        self.wait_duration = 0
        self.max_allowed_errors = max_allowed_errors

        # Initialize Status Values
        self.startup_at = datetime.now()
        self.entered_state_at = datetime.now()
        self.last_state = str(self.state)
        self.executed = Table(names=('type', 'target', 'pattern', 'instconfig',
                                     'detconfig', 'failed'),
                              dtype=('a20', 'a40', 'a20', 'a40', 'a40',
                                     np.bool))
        self.current_OB = None
        self.we_are_done = False
        self.durations = {}
        self.errors = []
        self.error_count = 0
        self.software_errors = []

        # Configure telescope hardware
        self.log(f'Sending location info to mount')
        self.telescope.set_sitelatitude(lat)
        assert np.isclose(lat, self.telescope.sitelatitude())
        self.telescope.set_sitelongitude(lon)
        assert np.isclose(lon, self.telescope.sitelongitude())
        self.telescope.set_siteelevation(height)
        assert np.isclose(height, self.telescope.siteelevation())
        self.log(f'Sending date and time to mount')
        now = datetime.utcnow()
        self.log(f'Computer time: {now.isoformat()}')
        self.telescope.set_utcdate(f"{now.isoformat()}Z")
        mountnow_str = self.telescope.utcdate()
        self.log(f'Mount time: {mountnow_str}')
        while len(mountnow_str) < 23:
            mountnow_str += '0'
        mountnow = datetime.fromisoformat(mountnow_str)
        dt = mountnow - now
        assert dt.total_seconds() < 0.25

        # Mongo Connection
        self.mongoIP = mongoIP
        self.mongoport = mongoport
        try:
            self.client = pymongo.MongoClient(mongoIP, mongoport)
            self.db = self.client[name]
            self.status_collection = self.db['status']
            self.log(f'Connected to Mongo DB')
        except:
            self.client = None
            self.db = None
            self.status_collection = None
            self.log(f'Failed to connect to Mongo DB', level=WARNING)
Пример #13
0

class Game(object):
    def return_true(self):
        return True

    def return_false_v1(self):
        return False

    def return_false_v2(self):
        return False


life_bot = Game()
machine = Machine(model=life_bot,
                  states=states,
                  transitions=transitions,
                  initial="first")

# method to do transition
print(life_bot.state)

life_bot.go_to_second()
life_bot.go_to_third()

print(life_bot.state)

life_bot.to_first()
print(life_bot.state)

life_bot.trigger("go_to_second")
print(life_bot.state)
Пример #14
0
    def __init__(self, **machine_configs):
        self.machine = GraphMachine(model=self, **machine_configs)

        self.__Hand = NhHand()
        self.__Eyes = NhEyes()
        self.__Reply = NhReply()
Пример #15
0
 def __init__(self, **machine_configs):
     self.machine = GraphMachine(model=self, **machine_configs)
Пример #16
0
 def __init__(self, **machine_configs):
     self.machine = GraphMachine(ignore_invalid_triggers=True,
                                 model=self,
                                 **machine_configs)
Пример #17
0
 def __init__(self):
     self.machine = GraphMachine(
         model = self,
         states=[
             'welcome',
             'entry',
             'outside',
             'intro',
             'slot',
             'inside',
             'service',
             'google',
             'youtube',
             'screenshot'
         ],
         transitions=[
             {
                 'trigger': 'forward', 'conditions': 'going_outside',
                 'source': 'entry', 'dest': 'outside'
             },
             {
                 'trigger': 'forward', 'conditions': 'going_intro',
                 'source': 'outside', 'dest': 'intro'
             },
             {
                 'trigger': 'forward', 'conditions': 'going_slot',
                 'source': 'intro', 'dest': 'slot'
             },
             {
                 'trigger': 'forward', 'conditions': 'going_slot',
                 'source': 'slot', 'dest': 'slot'
             },
             {
                 'trigger': 'forward', 'conditions': 'going_inside',
                 'source': 'entry', 'dest': 'inside'
             },
             {
                 'trigger': 'wrong',
                 'source': 'inside', 'dest': 'outside'
             },
             {
                 'trigger': 'forward', 'conditions': 'going_service',
                 'source': 'inside', 'dest': 'service'
             },
             {
                 'trigger': 'forward', 'conditions': 'going_google',
                 'source': 'service', 'dest': 'google'
             },
             {
                 'trigger': 'forward', 'conditions': 'google_repeat',
                 'source': 'google', 'dest': 'google'
             },
             {
                 'trigger': 'forward', 'conditions': 'going_youtube',
                 'source': 'service', 'dest': 'youtube'
             },
             {
                 'trigger': 'forward', 'conditions': 'youtube_repeat',
                 'source': 'youtube', 'dest': 'youtube'
             },
             {
                 'trigger': 'forward', 'conditions': 'going_screenshot',
                 'source': 'service', 'dest': 'screenshot'
             },
             {
                 'trigger': 'forward', 'conditions': 'screenshot_repeat',
                 'source': 'screenshot', 'dest': 'screenshot'
             },
             {
                 'trigger': 'back',
                 'source': 'intro', 'dest': 'outside'
             },
             {
                 'trigger': 'back',
                 'source': 'slot', 'dest': 'intro'
             },
             {
                 'trigger': 'back',
                 'source': 'service', 'dest': 'inside'
             },
             {
                 'trigger': 'back',
                 'source': ['google', 'youtube', 'screenshot'], 'dest': 'service'
             },
             {
                 'trigger': 'restart',
                 'source': [
                     'welcome',
                     'outside',
                     'intro',
                     'slot',
                     'inside',
                     'service',
                     'google',
                     'youtube',
                     'screenshot'
                 ],
                 'dest': 'entry'
             }
         ],
         initial='welcome',
         auto_transitions=True,
         show_conditions=True
     )