diff --git a/Tutu2Alice.zip b/Tutu2Alice.zip index 4222eb2..2fcdae6 100644 Binary files a/Tutu2Alice.zip and b/Tutu2Alice.zip differ diff --git a/ability_000.py b/ability_000.py index b998323..6ff768d 100644 --- a/ability_000.py +++ b/ability_000.py @@ -3,9 +3,111 @@ from datetime import datetime from bs4 import BeautifulSoup from pytz import timezone +import logging -class ScheduleParser: +def parse_tuturu(station_1: str, station_2: str): + # Определяем URL сайта + url = f'https://www.tutu.ru/prigorod/search.php?st1={quote(station_1)}&st1_num=&st2={quote(station_2)}&st2_num=' + logging.debug(f"go to url {url}") + # Отправляем запрос + response = requests.get(url) + + # Проверяем статус ответа + if response.status_code != 200: + logging.debug("not 200 from tutu.ru") + logging.debug(f"status_code: {response.status_code}") + logging.debug(f"text: {response.text}") + + return + else: + # Используем библиотеку BeautifulSoup для парсинга HTML + + # Создаем объект BeautifulSoup + # Находим нужный элемент на странице + soup = BeautifulSoup(response.text, 'html.parser') + elements_from = soup.find_all('a', class_='g-link desktop__depTimeLink__1NA_N') + + # Создаем объект BeautifulSoup + # Находим нужный элемент на странице + soup = BeautifulSoup(response.text, 'html.parser') + elements_to = soup.find_all('a', class_='g-link desktop__arrTimeLink__2TJxM') + + # Выводим найденные элементы + logging.debug(f"len1 ({len(elements_from)}), len2 ({len(elements_to)})") + if len(elements_from) != len(elements_to) or len(elements_from) == 0 or len(elements_to) == 0: + logging.debug('Произошла ошибка len') + return + + list_of_tuples = [] + for i in range(len(elements_from)): + list_of_tuples.append((elements_from[i].text, elements_to[i].text)) + + # logging.debug(list_of_tuples) + return list_of_tuples + + +def final_response_text(all_today_s_trips, timezone_str): + # Получение текущего времени + local_tz = timezone(timezone_str) + current_time = datetime.now(local_tz).strftime("%H:%M") + + # Поиск индекса элемента, время которого больше текущего + index = next((i for i, trip in enumerate(all_today_s_trips) if trip[0] > current_time), None) + + if index is not None: + trips = [[all_today_s_trips[index][0], all_today_s_trips[index][1]]] + + if index + 1 < len(all_today_s_trips): + trips.append([all_today_s_trips[index + 1][0], all_today_s_trips[index + 1][1]]) + if index + 2 < len(all_today_s_trips): + trips.append([all_today_s_trips[index + 2][0], all_today_s_trips[index + 2][1]]) + + trips_timings = [ + ( + calculate_time_difference(current_time, item[0]), + calculate_time_difference(item[0], item[1]) + ) + for i, item in enumerate(trips) + ] + + prefix_templates = [ + "Ближайшее отправление", + "Второе ближайшее отправление", + "Третье ближайшее отправление" + ] + + response_text = "" + for i in range(len(trips)): + # Второе ближайшее отправление: через 10 минут в 12:00 с прибытием 13:00, 60 минут в пути. + line = prefix_templates[i] + ": через " + trips_timings[i][0] + \ + " минут в " + trips[i][0] + " с прибытием " + trips[i][1] + \ + ", " + trips_timings[i][1] + " минут в пути. " + + response_text += line + + logging.debug(trips) + logging.debug(trips_timings) + + return response_text + + # is here if (if index is not None) didn't work + logging.debug("is here if (if index is not None) didn't work") + logging.debug("Can't find a trip") + return + + +def calculate_time_difference(time1, time2): + format_str = "%H:%M" + datetime1 = datetime.strptime(time1, format_str) + datetime2 = datetime.strptime(time2, format_str) + time_diff = datetime2 - datetime1 + minutes_diff = time_diff.total_seconds() // 60 + return str(int(minutes_diff)) + + +# LEGACY CODE +class _ScheduleParser: def __init__(self, station_1, station_2): self.station_1 = station_1 self.station_2 = station_2 diff --git a/index.py b/index.py index cd13c06..83cc349 100644 --- a/index.py +++ b/index.py @@ -1,83 +1,52 @@ -from ability_000 import ScheduleParser +from reactions import * +import logging +logging.getLogger().setLevel(logging.DEBUG) -def handler(event, context): - """ - Entry-point for Serverless Function. - :param event: request payload. - :param context: information about current execution context. - :return: response to be serialized as JSON. - """ +def handler(event, context): + logging.debug("start") try: - if 'session' in event and 'message_id' in event['session']: - - match event['session']['message_id']: - # Назовите Станцию Отправления - case 0: - - text = 'Назовите Станцию Отправления:' - end_session = 'false' - session_dict = None - - # Назовите Станцию Прибытия - case 1: - - if 'request' in event and \ - 'original_utterance' in event['request'] \ - and len(event['request']['original_utterance']) > 0: - - text = 'Назовите Станцию Прибытия:' - end_session = 'false' - session_dict = {'st1': event['request']['original_utterance']} - - # Возврат расписания - case 2: - - if 'request' in event and \ - 'original_utterance' in event['request'] \ - and len(event['request']['original_utterance']) > 0: - - if 'state' in event and \ - 'session' in event['state'] and \ - 'st1' in event['state']['session']: - st1 = event['state']['session']['st1'] - else: - raise 'State error' - - st2 = event['request']['original_utterance'] - - try: - schedule = ScheduleParser(str(st1), str(st2)).parse() - - if schedule["error"] is None: - text = 'Ближайшее отправление: ' + schedule['r1'][0] + ' с прибытием ' + schedule['r1'][1] + \ - '. Второе ближайшее отправление: ' + schedule['r2'][0] + ' с прибытием ' + schedule['r2'][1] + \ - '. Третье ближайшее отправление: ' + schedule['r3'][0] + ' с прибытием ' + schedule['r3'][1] - end_session = 'true' - session_dict = None - else: - text = schedule['error'] - end_session = 'true' - session_dict = None - except Exception as e: - text = str(e) - end_session = 'true' - session_dict = None + if 'request' in event and \ + 'command' in event['request']: + + logging.debug("command in request; matching...") + match event['request']['command']: + case "": + return answer_on_init(event, context) + case "помощь": + return answer_on_help(event, context) + case "что ты умеешь": + return answer_on_what(event, context) + case _: + logging.debug("not _ or h or w; searching for st1...") + if 'state' in event and \ + 'session' in event['state'] and \ + 'st1' in event['state']['session']: + logging.debug("st1 in state") + return answer_on_st2(event, context) + else: + logging.debug("st1 not in state") + return answer_on_st1(event, context) except Exception as e: - text = str(e) - end_session = 'true' - session_dict = None - - return { - 'version': event['version'], - 'session': event['session'], - 'response': { - # Respond with the original request or welcome the user if this is the beginning of the dialog and the request has not yet been made. - 'text': text, - # Don't finish the session after this response. - 'end_session': end_session - }, - 'session_state': session_dict - } + logging.debug(e) + return { + 'version': event['version'], + 'session': event['session'], + 'response': { + 'text': e, + 'end_session': 'true' + }, + } + + else: + logging.debug("idk") + return { + 'version': event['version'], + 'session': event['session'], + 'response': { + 'text': "idk", + 'end_session': 'true' + }, + } diff --git a/index_old.py b/index_old.py new file mode 100644 index 0000000..cd13c06 --- /dev/null +++ b/index_old.py @@ -0,0 +1,83 @@ +from ability_000 import ScheduleParser + + +def handler(event, context): + """ + Entry-point for Serverless Function. + :param event: request payload. + :param context: information about current execution context. + :return: response to be serialized as JSON. + """ + + try: + if 'session' in event and 'message_id' in event['session']: + + match event['session']['message_id']: + # Назовите Станцию Отправления + case 0: + + text = 'Назовите Станцию Отправления:' + end_session = 'false' + session_dict = None + + # Назовите Станцию Прибытия + case 1: + + if 'request' in event and \ + 'original_utterance' in event['request'] \ + and len(event['request']['original_utterance']) > 0: + + text = 'Назовите Станцию Прибытия:' + end_session = 'false' + session_dict = {'st1': event['request']['original_utterance']} + + # Возврат расписания + case 2: + + if 'request' in event and \ + 'original_utterance' in event['request'] \ + and len(event['request']['original_utterance']) > 0: + + if 'state' in event and \ + 'session' in event['state'] and \ + 'st1' in event['state']['session']: + st1 = event['state']['session']['st1'] + else: + raise 'State error' + + st2 = event['request']['original_utterance'] + + try: + schedule = ScheduleParser(str(st1), str(st2)).parse() + + if schedule["error"] is None: + text = 'Ближайшее отправление: ' + schedule['r1'][0] + ' с прибытием ' + schedule['r1'][1] + \ + '. Второе ближайшее отправление: ' + schedule['r2'][0] + ' с прибытием ' + schedule['r2'][1] + \ + '. Третье ближайшее отправление: ' + schedule['r3'][0] + ' с прибытием ' + schedule['r3'][1] + end_session = 'true' + session_dict = None + else: + text = schedule['error'] + end_session = 'true' + session_dict = None + except Exception as e: + text = str(e) + end_session = 'true' + session_dict = None + + except Exception as e: + text = str(e) + end_session = 'true' + session_dict = None + + return { + 'version': event['version'], + 'session': event['session'], + 'response': { + # Respond with the original request or welcome the user if this is the beginning of the dialog and the request has not yet been made. + 'text': text, + # Don't finish the session after this response. + 'end_session': end_session + }, + 'session_state': session_dict + } diff --git a/reactions.py b/reactions.py new file mode 100644 index 0000000..5580bbc --- /dev/null +++ b/reactions.py @@ -0,0 +1,86 @@ +from ability_000 import parse_tuturu, final_response_text +import logging + + +def answer_on_init(event, context): + text = "Здравствуйте, это навык для получения расписания отхода ближайших электричек. Назовите станцию отправления:" + + logging.debug(text) + return { + 'version': event['version'], + 'session': event['session'], + 'response': { + 'text': text, + 'end_session': 'false' + }, + } + + +def answer_on_help(event, context): + text = """С помощью этого навыка можно узнать расписание отхода ближайших электричек. Сначала навык узнает у вас станцию отправления, затем станцию прибытия. + Примеры названий станций: Москва, Москва-Ярославская, Красный балтиец. + Текущее время определяется автоматически, в зависимости от часового пояса вашего устройства. + После ответа с расписанием навык прекращает работу. + Вы можете назвать станцию отправления прямо сейчас:""" + + logging.debug(text) + return { + 'version': event['version'], + 'session': event['session'], + 'response': { + 'text': text, + 'end_session': 'false' + }, + } + + +def answer_on_what(event, context): + text = """С помощью этого навыка можно узнать расписание отхода ближайших электричек. Сначала навык узнает у вас станцию отправления, затем станцию прибытия. + Примеры названий станций: Москва, Москва-Ярославская, Красный балтиец. + Текущее время определяется автоматически, в зависимости от часового пояса вашего устройства. + После ответа с расписанием навык прекращает работу. + Вы можете назвать станцию отправления прямо сейчас:""" + + logging.debug(text) + return { + 'version': event['version'], + 'session': event['session'], + 'response': { + 'text': text, + 'end_session': 'false' + }, + } + + +def answer_on_st1(event, context): + text = "Назовите станцию прибытия:" + st1 = event['request']['command'] + + logging.debug(text) + return { + 'version': event['version'], + 'session': event['session'], + 'response': { + 'text': text, + 'end_session': 'false' + }, + 'session_state': {'st1': st1} + } + + +def answer_on_st2(event, context): + st1 = event['state']['session']['st1'] + st2 = event['request']['command'] + local_tz = event["meta"]["timezone"] + + text = final_response_text(parse_tuturu(st1, st2), local_tz) + + logging.debug(text) + return { + 'version': event['version'], + 'session': event['session'], + 'response': { + 'text': text, + 'end_session': 'true' + }, + } diff --git a/requirements.txt b/requirements.txt index de3b98b..3991e9e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,7 +2,6 @@ beautifulsoup4==4.12.2 certifi==2023.7.22 charset-normalizer==3.2.0 idna==3.4 -lxml==4.9.3 requests==2.31.0 soupsieve==2.4.1 urllib3==2.0.4 diff --git a/test.py b/test_old.py similarity index 100% rename from test.py rename to test_old.py diff --git a/zipler.py b/zipler.py new file mode 100644 index 0000000..40a699b --- /dev/null +++ b/zipler.py @@ -0,0 +1,13 @@ +import zipfile + + +def create_zip_archive(file_paths, zip_file_name): + with zipfile.ZipFile(zip_file_name, 'w') as zipf: + for file_path in file_paths: + zipf.write(file_path) + + +# Пример использования +file_paths = ['ability_000.py', 'requirements.txt', 'index.py', 'reactions.py'] +zip_file_name = 'Tutu2Alice.zip' +create_zip_archive(file_paths, zip_file_name)