Пример #1
0
def expect_departure_time(turn: RzdTurn):
    print("expect_departure_time handler")

    # Должен быть заполнен интент slots_filing и слот when
    forms = turn.forms['slots_filling']
    when_text = forms.get('when', None)
    near_text = forms.get("near", None)

    if not (when_text or near_text):
        # Во время дозаполнения слота времени мы не получили данный слот. Переспрашиваем ещё раз
        turn.response_text = 'Назовите дату, на которую хотите посмотреть билет'
        # Оставляем тот же стейт
        turn.stage = 'expect_departure_time'
        turn.suggests.extend(['Завтра', 'Сегодня', 'Ближайшая'])
    elif when_text:
        # Получили недостающий слот со временем. Заполняем данные
        turn.user_object['when_text'] = date2ru(convert_date_to_abs(when_text))
        turn = check_slots_and_chose_state(turn)
        print(f"turn.response_text: {turn.response_text}")
    else:
        turn.user_object['near_text'] = near_text
        # На всякий случай сохраняем в поле when_text сегодняшнюю дату
        turn.user_object['when_text'] = date2ru(convert_date_to_abs('сегодня'))
        turn = check_slots_and_chose_state(turn)
        print(f"turn.response_text: {turn.response_text}")
Пример #2
0
def expect_slots_and_choose_state_for_selecting_train(turn: RzdTurn):
    """Второй этап. Заполнение слотов для выбора поезда."""
    print(f"usr_object: {turn.user_object}")

    # Достаем все возможные слоты из объекта
    trains = turn.user_object.get("trains", None)
    car_type = turn.user_object.get("car_type", None)
    seat_type = turn.user_object.get("seat_type", None)

    print(f"car_type: {car_type}")
    print(f"seat_type: {seat_type}")

    if car_type and (seat_type or car_type not in ['econom', 'sleeping']):
        filtered_trains = filter_trains_by_rzd_car_type(
            trains, car_type_to_rzd_type(car_type))
        if not filtered_trains:
            turn.response_text = f'К сожалению нет поездов с {car_type_to_human_str(car_type, form=2)} вагонами ' \
                                 f'на указанное время.\n' \
                                 f'Давайте выберем вагон другого типа?'
            turn.suggests.extend(get_human_readable_existing_car_types(trains))
            turn.user_object["car_type"] = None
            turn.stage = "expect_car_type"
        else:
            selected_train = filtered_trains[0]
            turn.user_object["selected_train"] = selected_train

            response_str = f'Покупаем билет на поезд {selected_train["number"]} ' \
                           f'{selected_train["from"]} {selected_train["to"]} на {selected_train["time_start"]} ' \
                           f'местного времени, '
            if car_type in ['econom', 'sleeping']:
                response_str += f'{seat_type_to_human_str(seat_type, form=1)} место в ' \
                                f'{car_type_to_human_str(car_type, form=3)} вагоне. '
            else:
                response_str += f'{car_type_to_human_str(car_type)} вагон. '
            response_str += f'\nЦена {selected_train["cost"]} руб.\nВсе правильно?'

            turn.response_text = response_str
            turn.stage = 'expect_after_selecting_train_slots_filled'
            turn.suggests.extend(['Да', 'Нет'])

            # Заполнили вторую часть

    if not car_type:
        extracted_prices = extract_min_max_prices_for_car_types(trains)
        prices_information_str = extracted_prices_to_information_str(
            extracted_prices)
        turn.response_text = f'{prices_information_str}\nКакой хотите тип вагона?'
        turn.stage = 'expect_car_type'
        turn.suggests.extend(get_human_readable_existing_car_types(trains))
    elif car_type in ['econom', 'sleeping'] and not seat_type:
        # Для плацкартного и купейного вагона уточняем тип места
        turn.response_text = 'Верхнее или нижнее место?'
        turn.stage = 'expect_seat_type'
        turn.suggests.extend(['Верхнее', 'Нижнее'])

    print(f"next stage is: {turn.stage}")
    return turn
Пример #3
0
def expect_departure_time_tag(turn: RzdTurn):
    print("expect_departure_time_tag handler")
    print(f"intents: {turn.intents}")

    forms = turn.forms['time_tags']
    time_tags = forms.keys()

    source_trains = turn.user_object['trains']
    filtered_trains = filter_trains_by_time_tags(source_trains, time_tags)

    print(f"Filtered trains by time tags: {filtered_trains}")

    if len(filtered_trains) == 0:
        extracted_prices = extract_min_max_prices_for_car_types(source_trains)
        prices_information_str = extracted_prices_to_information_str(
            extracted_prices)

        # Если на нужное время дня нет билетов то говорим об этом и предлагаем соазу выбирать тип вагона и места
        turn.response_text = f'К сожалению, на выбранное время дня нет билетов. ' \
                             f'Давайте выберем место в найденных билетах?\n\n{prices_information_str}'
        turn.stage = 'expect_all_train_data'

        rzd_car_types = get_human_readable_existing_car_types(source_trains)
        if rzd_car_types:
            turn.suggests.extend(
                create_suggestions_for_car_types(rzd_car_types))
        else:
            turn.suggests.extend(
                ['Верхнее место в плацкарте', 'Нижнее место в купе'])
    else:
        extracted_prices = extract_min_max_prices_for_car_types(
            filtered_trains)
        prices_information_str = extracted_prices_to_information_str(
            extracted_prices)

        # Теперь в юзерстейте лежат отфильтрованные по времени билеты
        turn.user_object['trains'] = filtered_trains
        # Переходим к выбору типа вагона и мест
        turn.response_text = f'{prices_information_str}\nКакое место хотите?'
        turn.stage = 'expect_all_train_data'

        rzd_car_types = get_human_readable_existing_car_types(filtered_trains)
        if rzd_car_types:
            turn.suggests.extend(
                create_suggestions_for_car_types(rzd_car_types))
        else:
            turn.suggests.extend(
                ['Верхнее место в плацкарте', 'Нижнее место в купе'])
Пример #4
0
def expect_all_train_data(turn: RzdTurn):
    print("expect_all_train_data handler")

    forms = turn.forms['selecting_train']

    car_type = forms.get('car_type', None)
    seat_type = forms.get('seat_type', None)

    if car_type:
        turn.user_object['car_type'] = car_type
    if seat_type:
        turn.user_object['seat_type'] = seat_type

    turn = expect_slots_and_choose_state_for_selecting_train(turn)
    print(f"expect_all_train_data response_text: {turn.response_text}")
    print(f"expect_all_train_data stage: {turn.stage}")
Пример #5
0
def expect_departure_time(turn: RzdTurn):
    print("expect_departure_time handler")

    # Должен быть заполнен интент slots_filing и слот when
    forms = turn.forms['slots_filing']
    when_text = forms.get('when', None)

    if not when_text:
        # Во время дозаполнения слота времени мы не получили данный слот. Переспрашиваем ещё раз
        turn.response_text = 'Назовите дату, на которую хотите посмотреть билет'
        # Оставляем тот же стейт
        turn.stage = 'expect_departure_time'
        turn.suggests.extend(['Завтра', 'Сегодня'])
    else:
        # Получили недостающий слот со временем. Заполняем данные
        turn.ctx.user_object['when_text'] = when_text
Пример #6
0
def expect_destination_place(turn: RzdTurn):
    # Уточняем место назначения
    print("expect_destination_place handler")

    # Должен быть заполнен интент slots_filing и слот to
    forms = turn.forms['slots_filing']
    to_text = forms.get('to', None)

    if not to_text:
        # Во время дозаполнения слота места назначения мы не получили данный слот. Переспрашиваем ещё раз
        turn.response_text = 'Назовите конечный пункт куда вы держите путь'
        # Оставляем тот же стейт
        turn.stage = 'expect_destination_place'
        turn.suggests.extend(['Москва', 'Петербург'])
    else:
        # Получили недостающий слот со временем. Заполняем данные
        turn.ctx.user_object['to_text'] = to_text
Пример #7
0
def expect_departure_place(turn: RzdTurn):
    # Уточняем место отправления
    print("expect_departure_place handler")

    # Должен быть заполнен интент slots_filing и слот from
    forms = turn.forms['slots_filing']
    from_text = forms.get('from', None)

    if not from_text:
        # Во время дозаполнения слота места отправления мы не получили данный слот. Переспрашиваем ещё раз
        turn.response_text = 'Назовите, откуда вы собираетесь выезжать'
        # Оставляем тот же стейт
        turn.stage = 'expect_departure_place'
        turn.suggests.extend(['Москва', 'Петербург'])
    else:
        # Получили недостающий слот со временем. Заполняем данные
        turn.ctx.user_object['from_text'] = from_text
Пример #8
0
def suburb_purchase_details(turn: RzdTurn):
    sub = SuburbContext.from_dict(turn.user_object.get('suburb') or {})
    if turn.intents.get('both_sides'):
        sub.bidirectional = True
    if turn.intents.get('one_side'):
        sub.bidirectional = False
    turn.response_text = f'Покупаю билет от станции {sub.from_norm} до станции {sub.to_norm}, '
    if sub.bidirectional:
        turn.response_text += 'в обе стороны, '
    cost_text = with_number("рубль", sub.cost * (sub.bidirectional + 1))
    turn.response_text += f' с вашей карты {turn.bank_card} спишется {cost_text}. '
    turn.response_text += 'Вы подтверждаете покупку?'
    turn.suggests.append('Да')
    turn.suggests.append(
        'В одну сторону' if sub.bidirectional else 'В обе стороны')
    turn.stage = 'suburb_confirm_sell_final'
    turn.user_object['suburb'] = sub.to_dict()
Пример #9
0
def intercity_route(turn: RzdTurn, form=None):
    # the argument "form" is used when switching from suburban scenario
    print(f"intercity_route handler intents: {turn.intents}")
    forms = form or turn.forms['intercity_route']
    print(f"intercity_route handler forms: {forms}")
    # from_text = turn.ctx.yandex.request.nlu.intents['route_from_to'].slots['from']

    # Может быть заполнено от 1 до 3х форм: from, to, when
    from_text = forms.get('from', None)
    to_text = forms.get('to', None)
    when_text = forms.get('when', None)
    near_text = forms.get('near', None)

    print(f"near_text: {when_text}")

    if from_text:
        from_text = convert_geo_to_normalized_city(from_text)
        turn.user_object['from_text'] = from_text
        turn.user_object['from_id'] = suggest_first_station(from_text)
    if to_text:
        to_text = convert_geo_to_normalized_city(to_text)
        turn.user_object['to_text'] = to_text
        turn.user_object['to_id'] = suggest_first_station(to_text)
    if when_text:
        turn.user_object['when_text'] = date2ru(convert_date_to_abs(when_text))
    if near_text:
        # На всякий случай сохраняем в поле when_text сегодняшнюю дату
        turn.user_object['when_text'] = date2ru(convert_date_to_abs('сегодня'))
        turn.user_object['near_text'] = near_text

    turn = check_slots_and_chose_state(turn)
    print(f"turn.response_text: {turn.response_text}")
Пример #10
0
 def respond(self, ctx: Context):
     t0 = time.time()
     text, forms, intents = self.nlu(ctx)
     turn = RzdTurn(
         ctx=ctx,
         text=text,
         intents=intents,
         forms=forms,
         user_object=ctx.user_object,
         rasp_api=self.rasp_api,
         world=self.world,
     )
     logger.debug(f'current stage is: {turn.stage}')
     handler_name = self.cascade(turn)
     print(f"Handler name: {handler_name}")
     self.cascade.postprocess(turn)
     resp = turn.make_response()
     logger.debug(f'FULL RESPONSE TIME: {time.time() - t0}')
     return resp
Пример #11
0
    def respond(self, ctx: Context):
        forms = {}  # todo: extract our own forms with regexes
        if ctx.yandex:
            forms = extract_yandex_forms(ctx.yandex)
        intents = {intent_name: 1 for intent_name in forms}
        if tgalice.nlu.basic_nlu.like_help(ctx.message_text):
            intents['help'] = 1

        print(f"Intents: {intents}")
        print(f"Forms: {forms}")
        turn = RzdTurn(ctx=ctx,
                       text=fast_normalize(ctx.message_text),
                       intents=intents,
                       forms=forms)
        handler_name = self.cascade(turn)
        print(f"Handler name: {handler_name}")
        self.cascade.postprocess(turn)
        print()
        return turn.make_response()
Пример #12
0
def expect_seat_type(turn: RzdTurn):
    # Уточняем тип места
    print("expect_seat_type handler")

    forms = turn.forms.get('selecting_train', None) or turn.forms.get(
        'seat_type_slot_filling', None)
    seat_type = forms.get('seat_type', None)
    car_type = turn.user_object.get('car_type', None)

    if car_type == 'first_class' or car_type == 'seating':
        turn.user_object['seat_type'] = 'нет'
    elif not seat_type:
        # Для плацкартного и купейного вагона уточняем тип места
        turn.response_text = 'Верхнее или нижнее место?'
        turn.stage = 'expect_seat_type'
        turn.suggests.extend(['Верхнее', 'Нижнее'])
    else:
        # Получили недостающий слот со временем. Заполняем данные
        turn.user_object['seat_type'] = seat_type
        turn = expect_slots_and_choose_state_for_selecting_train(turn)
        print(f"turn.response_text: {turn.response_text}")
Пример #13
0
def expect_car_type(turn: RzdTurn):
    # Уточняем тип вагона
    print("expect_car_type handler")

    # Должен быть заполнен интент selecting_train и слот car_type
    forms = turn.forms.get('selecting_train', None) or turn.forms.get(
        'car_type_slot_filling', None)
    car_type = forms.get('car_type', None)
    trains = turn.user_object.get("trains", None)

    prices_information_str = ""
    if trains:
        extracted_prices = extract_min_max_prices_for_car_types(trains)
        prices_information_str = extracted_prices_to_information_str(
            extracted_prices)

    if not car_type:
        # Переспрашиваем тип вагона

        turn.response_text = f'{prices_information_str}Какой хотите тип вагона?'
        # Оставляем тот же стейт
        turn.stage = 'expect_car_type'
        if trains:
            turn.suggests.extend(get_human_readable_existing_car_types(trains))
        else:
            turn.suggests.extend(['Плацкартный', 'Купейный', 'СВ', 'Сидячий'])
    elif trains and not car_type_to_human_str(car_type).capitalize(
    ) in get_human_readable_existing_car_types(trains):
        # Гооворим, что вагона заданного типа нет
        turn.response_text = f'К сожалению нет поездов с {car_type_to_human_str(car_type, form=2)} вагонами ' \
                             f'на указанное время! ' \
                             f'Выберем другой тип вагона?\n\n{prices_information_str}'
        # Оставляем тот же стейт
        turn.stage = 'expect_car_type'
        turn.suggests.extend(get_human_readable_existing_car_types(trains))
    else:
        # Получили недостающий слот со временем. Заполняем данные
        turn.user_object['car_type'] = car_type
        turn = expect_slots_and_choose_state_for_selecting_train(turn)
        print(f"turn.response_text: {turn.response_text}")
Пример #14
0
def expect_destination_place(turn: RzdTurn):
    # Уточняем место назначения
    print("expect_destination_place handler")

    # Должен быть заполнен интент slots_filing и слот to
    forms = turn.forms['slots_filling']
    to_text = forms.get('to', None) or forms.get('place', None) or forms.get(
        'from', None)

    if not to_text:
        # Во время дозаполнения слота места назначения мы не получили данный слот. Переспрашиваем ещё раз
        turn.response_text = 'Назовите конечный пункт куда вы держите путь'
        # Оставляем тот же стейт
        turn.stage = 'expect_destination_place'
        turn.suggests.extend(['Москва', 'Петербург'])
    else:
        # Получили недостающий слот с местом назначения. Заполняем данные
        to_text = convert_geo_to_normalized_city(to_text)
        turn.user_object['to_text'] = to_text
        turn.user_object['to_id'] = suggest_first_station(to_text)
        turn = check_slots_and_chose_state(turn)
        print(f"turn.response_text: {turn.response_text}")
Пример #15
0
def expect_departure_place(turn: RzdTurn):
    # Уточняем место отправления
    print("expect_departure_place handler")

    # Должен быть заполнен интент slots_filing и слот from
    forms = turn.forms['slots_filling']
    from_text = forms.get('from', None) or forms.get(
        'place', None) or forms.get('to', None)

    if not from_text:
        # Во время дозаполнения слота места отправления мы не получили данный слот. Переспрашиваем ещё раз
        turn.response_text = 'Назовите, откуда вы собираетесь выезжать'
        # Оставляем тот же стейт
        turn.stage = 'expect_departure_place'
        turn.suggests.extend(['Москва', 'Петербург'])
    else:
        # Получили недостающий слот с местом отправления. Заполняем данные
        from_text = convert_geo_to_normalized_city(from_text)
        turn.user_object['from_text'] = from_text
        turn.user_object['from_id'] = suggest_first_station(from_text)
        turn = check_slots_and_chose_state(turn)
        print(f"turn.response_text: {turn.response_text}")
Пример #16
0
def suburb_confirm_purchase(turn: RzdTurn):
    sub = SuburbContext.from_dict(turn.user_object.get('suburb') or {})
    p = int(sub.cost) * (1 + sub.bidirectional)
    url = f'https://rzd-skill.herokuapp.com/qr/?f={sub.from_norm}&t={sub.to_norm}'
    text = f'Отлично! Я оформила вам билет на электричку. ' \
           f'Вы можете распечатать его или приложить QR-код прямо к турникету.'
    turn.response = Response(
        image=BigImage(
            image_id='213044/4e2dacacedfb7029f89e',
            button_text='Скачать билет',
            button_url=url,
            description=text,
        ),
        text='',  # voice='*',
        rich_text=text,
        links=[{
            'hide': True,
            'text': 'Скачать билет',
            'url': url
        }],
    )
    turn.response.image_url = 'https://raw.githubusercontent.com/avidale/rzd-russian-bots/master/static/qr.png'
    turn.stage = 'suburb_after_selling'
Пример #17
0
def check_slots_and_chose_state(turn: RzdTurn):
    """Проверяем что заполнены все слоты. Если что-то не заполнено то выбираем какой заполнить."""
    print(turn.ctx.user_object)
    # Достаем все возможные слоты из объекта
    from_text = turn.ctx.user_object.get("from_text", None)
    to_text = turn.ctx.user_object.get("to_text", None)
    when_text = turn.ctx.user_object.get("when_text", None)

    if from_text and to_text and when_text:
        turn.response_text = f'Ищу билеты {from_text} {to_text} {when_text}. Все правильно?'
        turn.stage = 'expect_after_slots_filled'
        # На данном этапе полностью получены все слоты

    elif from_text and to_text:
        turn.response_text = f'Когда поедем {from_text} {to_text}?'
        turn.stage = 'expect_departure_time'
        turn.suggests.extend(['Завтра', 'Сегодня'])

    elif from_text and when_text:
        turn.response_text = f'Куда поедем {from_text} {when_text}?'
        turn.stage = 'expect_destination_place'
        turn.suggests.extend(['Петербург', 'Казань'])

    elif to_text and when_text:
        turn.response_text = f'Откуда поедем {to_text} {when_text}?'
        turn.stage = 'expect_departure_place'
        turn.suggests.extend(['Москва', 'Петербург'])

    else:
        turn.response_text = f'Давайте попробуем заново. Откуда и куда вы хотите билет?'
        turn.stage = 'expect_departure_place'
        turn.suggests.extend(['Москва', 'Петербург'])

    print(f"next stage is: {turn.stage}")

    return turn
Пример #18
0
def expect_after_selecting_train_slots_filled(turn: RzdTurn):
    print("expect_after_selecting_train_slots_filled handler")
    print(f"intents: {turn.intents}")

    # TODO разобраться и поправить с учетом адреса картинки
    if 'yes' in turn.intents:
        selected_train = turn.user_object["selected_train"]
        cost = selected_train["cost"]
        from_location = selected_train["from"]
        to_location = selected_train["to"]
        url = f'https://rzd-skill.herokuapp.com/qr/?f={from_location}&t={to_location}'
        text = f'Отлично! Вы купили билет на поезд. С вашей карты будет списано {cost} руб.'
        turn.response = Response(
            image=BigImage(
                image_id='213044/4e2dacacedfb7029f89e',
                button_text='Скачать билет',
                button_url=url,
                description=text,
            ),
            text='',  # voice='*',
            rich_text=text,
        )
    else:
        turn.response_text = f'К сожалению, я ничего не нашла. Давайте попробуем заново'
Пример #19
0
def greeting_handler(turn: RzdTurn):
    # Очищаем некоторые данные о пользователе, например, заполненные слоты из предыдущей сессии
    user_slots_to_delete = [
        "from_text", "to_text", "when_text", "car_type", "seat_type",
        "quantity"
    ]
    for slot_name in user_slots_to_delete:
        if slot_name in turn.user_object:
            del turn.user_object[slot_name]

    turn.response_text = 'Привет! Это навык РЖД. Здесь вы можете найти и заказать билеты на поезд. ' \
                         'Чтобы выйти из навыка, скажите "Хватит".'
    turn.suggests.append('Помощь')
    turn.suggests.append('билет москва - ярославль')
    turn.suggests.append('электрички от беговой до сколково')

    for slot in TRANSIENT_SLOTS:
        if slot in turn.user_object:
            del turn.user_object[slot]
Пример #20
0
def fallback(turn: RzdTurn):
    turn.response_text = 'Простите, я вас не понимаю.'
    turn.suggests.append('Помощь')
    turn.suggests.append('билет москва - ярославль')
    turn.suggests.append('электрички от беговой до сколково')
Пример #21
0
def help_handler(turn: RzdTurn):
    # todo: make help dependent on turn.text and maybe some context
    turn.response_text = 'Это навык РЖД. Здесь вы можете найти и заказать билеты на поезд.' \
                         'Чтобы выйти из навыка, скажите "Хватит".'
    turn.suggests.append('Хватит')
Пример #22
0
def fallback(turn: RzdTurn):
    turn.response_text = 'Простите, я вас не понимаю.'
    turn.suggests.append('Помощь')
Пример #23
0
def greeting_handler(turn: RzdTurn):
    turn.response_text = 'Привет! Это навык РЖД. Здесь вы можете найти и заказать билеты на поиск.' \
                         'Чтобы выйти из навыка, скажите "Хватит".'
    turn.suggests.append('Помощь')
Пример #24
0
def check_slots_and_chose_state(turn: RzdTurn):
    """Проверяем что заполнены все слоты. Если что-то не заполнено то выбираем какой заполнить."""
    # Достаем все возможные слоты из объекта
    from_text = turn.user_object.get("from_text", None)
    to_text = turn.user_object.get("to_text", None)
    when_text = turn.user_object.get("when_text", None)
    near_text = turn.user_object.get("near_text", None)

    print(f"check_slots_and_chose_state from_text: {from_text}")
    print(f"check_slots_and_chose_state to_text: {to_text}")
    print(f"check_slots_and_chose_state when_text: {when_text}")
    print(f"check_slots_and_chose_state near_text: {near_text}")

    response_text = ""

    if from_text and to_text and (when_text or near_text):
        if near_text:
            audio = get_audio()
            timer = audio["medium"]
            turn.response_text = f'Ищу ближайшие билеты по маршруту {from_text} - {to_text}. {timer} ' \
                                 f'Показать, что нашлось?'
        else:
            turn.response_text = f'Ищу билеты по маршруту {from_text} - {to_text} {when_text}. Все правильно?'
        next_stage = 'expect_after_slots_filled'

        from_id = turn.user_object.get('from_id', None)
        to_id = turn.user_object.get('to_id', None)
        date_to = turn.user_object.get('when_text', None)
        params, cookies = init_find_route(from_id, to_id, date_to)
        turn.user_object['find_route_params'] = params
        turn.user_object['find_route_cookies'] = cookies

        # На данном этапе полностью получены все слоты
        turn.suggests.extend(['Да', 'Нет'])

    elif from_text and to_text and not (when_text or near_text):
        turn.response_text = f'Когда поедем по маршруту {from_text} - {to_text}?'
        next_stage = 'expect_departure_time'
        turn.suggests.extend(['Завтра', 'Сегодня'])

    elif to_text and not from_text:
        turn.response_text = f'Откуда поедем до {inflect_case(to_text, "gent")}?'
        next_stage = 'expect_departure_place'
        turn.suggests.extend(['Москва', 'Петербург'])

    elif from_text and not to_text:
        turn.response_text = f'Куда поедем из {inflect_case(from_text, "gent")}?'
        next_stage = 'expect_destination_place'
        turn.suggests.extend(['Москва', 'Петербург'])

    else:
        response_text = f'Давайте попробуем заново. Откуда и куда вы хотите билет?'
        # Либо пользователь скажет фразу целиком, либо как минимум станцию отправления
        # Тут косяк с тем, что получается он говорит станцию назначения (косяк из-за грамматики)
        next_stage = 'expect_departure_place'  # тут был None
        turn.suggests.extend(['Москва', 'Петербург'])

    # Если в ответе уже было сообщение (сообщение об ошибке), то текущий ответ просто добавляем
    # следующей строкой, иначе - заменяем
    if turn.response_text:
        turn.response_text += f'\n{response_text}'
    else:
        turn.response_text = response_text

    if next_stage:
        print(f"Next stage: {next_stage}")
        turn.stage = next_stage

    return turn
Пример #25
0
def suburb_route(turn: RzdTurn, force=False):
    form = turn.forms.get('suburb_route') or turn.forms.get(
        'suburb_route_rx') or turn.forms.get('suburb_ellipsis')
    # найди электрички от сколково до беговой turns to
    # {'suburb': 'электрички', 'from': 'беговой', 's9602218': 'сколково', 's9601666': 'беговой', 'to': 'сколково'}}
    sub = SuburbContext.from_dict(turn.user_object.get('suburb') or {})
    suburb_stage = (turn.stage or '').startswith('suburb')
    if not form:
        if suburb_stage:
            turn.response_text = 'Я не поняла вас. Пожалуйста, попробуйте переформулировать.'
        return
    if not suburb_stage \
            and not form.get('suburb') \
            and ('intercity_route' in turn.forms
                 or (turn.stage or '').startswith('expect') and 'slots_filling' in turn.forms) \
            and not force:
        # we give higher priority to intercity_route if we are out of suburb-specific context
        return

    text2slots = defaultdict(set)
    for k, v in form.items():
        if not isinstance(v, dict):  # skip yandex intents
            text2slots[v].add(k)
    if 'suburb_route_rx' in turn.forms and 'suburb_route' not in turn.forms and 'suburb_ellipsis' not in turn.forms:
        ft, fn = extract_slot_with_code('from', form, text2slots)
        tt, tn = extract_slot_with_code('to', form, text2slots)
    else:
        # yandex fills these slots in a really weird way
        ft, fn = form.get('from'), None
        tt, tn = form.get('to'), None

    if ft or tt:  # on new search, we don't want to keep bidirectionality
        sub.bidirectional = False

    center = None
    center_code = sub.from_code or sub.to_code or turn.last_yandex_code or 'c213'
    if center_code:
        c = turn.world.code2obj.get(center_code)
        if c and c.get('latitude'):
            center = c['latitude'], c['longitude']

    # matching stations or cities from Yandex queries
    if ft and not fn:
        fn = (turn.world.match(ft, center=center) or [None])[0]
        logger.debug('matched {} to {} '.format(
            ft, fn and turn.world.code2obj[fn]['title']))
    if tt and not tn:
        tn = (turn.world.match(tt, center=center) or [None])[0]
        logger.debug('matched {} to {} '.format(
            tt, tn and turn.world.code2obj[tn]['title']))

    logger.debug(f'{ft}  ({fn}) -> {tt} ({tn})')

    if fn:
        sub.from_text = ft
        sub.from_code = fn
    if tn:
        sub.to_text = tt
        sub.to_code = tn

    anchor = None
    if sub.from_code:
        anchor = turn.world.code2obj[sub.from_code]
    elif sub.to_code:
        anchor = turn.world.code2obj[sub.to_code]
    if anchor:
        now = local_now(lat=anchor['latitude'], lon=anchor['longitude'])
    else:
        now = datetime.now(tz=timezone('Europe/Moscow'))

    date = None
    if form.get('when'):
        date = convert_date_to_abs(form['when'])
    if not date and sub.date_txt:
        date = datetime.fromisoformat(sub.date_txt)
    if not date:
        date = now
    sub.date_txt = date.isoformat()
    logger.debug('now is {}, search date is {}'.format(now, date))

    if sub.from_code and sub.to_code and sub.from_code == sub.to_code:
        turn.response_text = f'Кажется точки отправления и назначения совпадают: {sub.from_text}. Попробуйте ещё раз.'
        turn.user_object['suburb'] = sub.to_dict()
        turn.suggests.append('электрички от тушино до красногорска')
        return

    if sub.from_code and sub.to_code:
        if form.get('back'):
            logger.debug('turning the route backwards!')
            sub.from_code, sub.to_code = sub.to_code, sub.from_code
            sub.from_text, sub.to_text = sub.to_text, sub.from_text

        result = turn.rasp_api.suburban_trains_between(
            code_from=sub.from_code,
            code_to=sub.to_code,
            date=str(date)[:10],
        )
        if not result:
            turn.response_text = f'Не удалось получить маршрут электричек от {sub.from_text} до {sub.to_text}.' \
                                 f' Поискать междугородные поезда? '
            turn.suggests.append('да')
            turn.stage = 'suggest_intercity_route_from_suburban'
            turn.user_object['suburb'] = sub.to_dict()
            return
        segments = result['segments']
        search = result['search']
        sub.from_norm = search['from']['title']
        sub.to_norm = search['to']['title']

        if segments and os.getenv('USE_CPPK_PRICES'):
            cost = get_cppk_cost(from_text=sub.from_text,
                                 to_text=sub.to_text,
                                 date=None,
                                 return_price=True)
        else:
            cost = None

        dur_text = None
        dur_minute = None
        durs = [r.get('duration') for r in segments if r.get('duration')]
        if durs:
            min_dur = int(min(durs) / 60)
            max_dur = int(max(durs) / 60)
            logger.debug(f'durations: min {min_dur} max {max_dur}')
            if max_dur / min_dur < 1.1:
                dur_text = f'{human_duration(max_dur)}'
            elif min_dur < 60 and max_dur < 60:
                dur_text = f'от {min_dur} до {human_duration(max_dur)}'
            else:
                dur_text = f'от {human_duration(min_dur)} до {human_duration(max_dur)}'
            dur_minute = min_dur
            if not cost:
                # approximate cost by duration
                cost = int(max(14.0, -10 + 2.25 * dur_minute))

        turn.response_text = phrase_results(
            name_from=sub.from_norm,
            name_to=sub.to_norm,
            results=result,
            only_next=(str(date)[:10] == str(now)[:10]),
            from_meta=turn.world.code2obj.get(sub.from_code),
            date=date,
            now=now,
            dur_text=dur_text,
        )
        if cost and segments:
            sub.cost = cost
            turn.response_text += f' Стоимость {with_number("рубль", cost)} в одну сторону. Желаете купить билет?'
            turn.stage = 'suburb_confirm_sell'
            turn.suggests.append('Да')
            turn.suggests.append('Туда и обратно')
        elif not segments:
            turn.response_text += ' Поискать междугородные поезда?'
            turn.suggests.append('да')
            turn.stage = 'suggest_intercity_route_from_suburban'
        else:
            turn.stage = 'suburb_no_price'
        turn.suggests.append('А обратно?')
        turn.suggests.append('А завтра?')
    elif not tn:
        turn.response_text = 'Куда вы хотите поехать' + (f' от станции {ft}'
                                                         if ft else '') + '?'
        turn.stage = 'suburb_get_to'
    elif not fn:
        turn.response_text = 'Откуда вы хотите поехать' + (f' до станции {tt}'
                                                           if tt else '') + '?'
        turn.stage = 'suburb_get_from'
    else:
        turn.response_text = 'Это какая-то невозможная ветка диалога'

    turn.user_object['suburb'] = sub.to_dict()
Пример #26
0
def expect_after_slots_filled(turn: RzdTurn):
    print("expect_after_slots_filled handler")
    print(f"intents: {turn.intents}")

    if 'yes' in turn.intents:
        # Дождались положительного ответа от пользователя после формирования полного запроса
        # Достаем результат
        trains = get_trains(turn)
        turn.user_object['trains'] = trains

        print("Trains received after API request")
        for train in trains:
            print(train)

        # Атрибуцируем тег времени для
        trains, time_tags = time_tag_attribution(trains)
        print(f"Trains with time tags:\n{trains}")
        print(trains)

        # Проверяем, если нет поездов
        if not trains:
            turn.response_text = f'К сожалению на выбранную дату нет поедов!'
            turn.user_object["when_text"] = None
            turn = check_slots_and_chose_state(turn)
        # Случай одного поезда
        elif len(trains) == 1:
            form_car_type = turn.forms.get('car_type', None)
            form_seat_type = turn.forms.get('seat_type', None)
            train_car_type = trains[0]['seat_type']
            turn.user_object['car_type'] = train_car_type
            turn.stage = 'expect_seat_type'
            # if train_car_type in ['Сидячий', 'СВ', 'Люкс']:
            #     turn.user_object['seat_type'] = 'нет'
            # else:
            #     turn.stage = 'expect_seat_type'
        # Случай нескольких поездов
        else:
            n_trains = len(trains)
            when_text = turn.user_object.get("when_text", None)
            turn.response_text = f'Найдено {with_number("поезд", n_trains)} на {when_text}.\n'

            min_cost, max_cost = get_min_max_costs(trains)
            min_time, max_time = get_min_and_max_departure_time(trains)

            if min_cost == max_cost:
                turn.response_text += f'Все белеты стоимостью {min_cost} руб.\n'
            else:
                turn.response_text += f"Билеты стоимостью от {min_cost} до {max_cost} руб.\n"
            if min_time == max_time:
                turn.response_text += f"Все поезда уходят в одно время {min_time}.\n"
            else:
                turn.response_text += f"Поезда ходят с {min_time} до {max_time}.\n"

            tag_names = [TIME_MAPPING[tag] for tag in time_tags]
            suggestion_tag_names = [
                SUGGESTION_TIME_MAPPING[tag] for tag in time_tags
            ]
            time_tags_str = ", ".join(tag_names)

            # Есть более одного выбора времени
            if len(time_tags) > 1:
                turn.response_text += f'\nЕсть поезда на {time_tags_str}. Когда желаете отправиться?'
                turn.stage = 'expect_departure_time_tag'
                turn.suggests.extend(suggestion_tag_names)
            else:
                # Проверяем, что тегов больше одног
                # Не даем выбрать время, потому что выбора нет
                extracted_prices = extract_min_max_prices_for_car_types(trains)
                prices_information_str = extracted_prices_to_information_str(
                    extracted_prices)
                turn.response_text += f'\nКакое место хотите?\n\n{prices_information_str}'
                turn.stage = 'expect_all_train_data'
                turn.suggests.extend([
                    'Верхнее место в плацкартном вагоне', 'Нижнее место в купе'
                ])
    else:
        turn.response_text = f'К сожалению, я ничего не нашла. Давайте попробуем заново'