-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathhomework.py
More file actions
182 lines (153 loc) · 6.95 KB
/
homework.py
File metadata and controls
182 lines (153 loc) · 6.95 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
import json
import logging
import os
import sys
import time
from http import HTTPStatus
from logging.handlers import RotatingFileHandler
import requests
import telegram
from dotenv import load_dotenv
import exception
from endpoint import ENDPOINT
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
formatter = logging.Formatter(
'%(asctime)s - %(funcName)s - [%(levelname)s] - %(message)s'
)
handler = logging.StreamHandler()
handler.setFormatter(formatter)
handler = RotatingFileHandler('main.log',
maxBytes=50000000,
backupCount=5,
encoding='UTF-8'
)
logger.addHandler(handler)
load_dotenv()
PRACTICUM_TOKEN = os.getenv('PRACTICUM_TOKEN')
TELEGRAM_TOKEN = os.getenv('TELEGRAM_TOKEN')
TELEGRAM_CHAT_ID = os.getenv('TELEGRAM_CHAT_ID')
TELEGRAM_RETRY_TIME = 600
HEADERS = {'Authorization': f'OAuth {PRACTICUM_TOKEN}'}
ENDPOINT
VERDICTS = {
'approved': 'Работа проверена: ревьюеру всё понравилось. Ура!',
'reviewing': 'Работа взята на проверку ревьюером.',
'rejected': 'Работа проверена: у ревьюера есть замечания.'
}
def send_message(bot, message) -> None:
"""
Отправляет сообщение в Телеграмм.
В случае неудачи, вызывает ошибку. Логирует события.
"""
failed_message = 'Не удалось отправить сообщение'
try:
bot.send_message(TELEGRAM_CHAT_ID, message)
logger.info(f'Бот отправил сообщение {message}')
except telegram.error.TelegramError:
logger.error(failed_message)
raise telegram.error.TelegramError(failed_message)
def get_api_answer(current_timestamp: int) -> dict:
"""
Делает запрос к API Практикум.
Возвращает ответ API преобразованный в тип данных Python.
Принимает в качестве параметра временную метку.
"""
timestamp = current_timestamp or int(time.time())
params = {'from_date': timestamp}
try:
response = requests.get(ENDPOINT, headers=HEADERS, params=params)
logger.info(f'Отправлен запрос к API Практикума. '
f'Код ответа API: {response.status_code}')
if response.status_code != HTTPStatus.OK:
raise response.raise_for_status()
except requests.exceptions.RequestException as error:
message = f'Эндпойнт недоступен: {error}'
logger.error(message)
raise requests.exceptions.RequestException(message)
except json.decoder.JSONDecodeError as error:
raise Exception((f'Ответ {response.text} получен не в виде JSON: '
f'{error}'))
return response.json()
def check_response(response: dict) -> list:
"""
Проверка корректности ответа API.
В качестве параметра получает ответ
API приведенный к типам данных Python (dict). Функция возвращает список
домашних работ по ключу 'homeworks'.
"""
logging.info('Начинаем проверку корректности ответа API.')
if isinstance(response, dict):
try:
homework = response['homeworks']
except KeyError as error:
message = f'В ответе не обнаружен ключ {error}'
logger.error(message)
raise exception.KeyNotFound(message)
if not isinstance(homework, list):
raise TypeError('Ответ не содержит список домашних работ')
message = 'Получены сведения о последней домашней работе'
logger.info(message) if len(homework) else None
return homework
else:
raise TypeError('В ответе API не обнаружен словарь')
def parse_status(homework):
"""Полученный ответ разделяем на имя и статус."""
"""И передаем его в сообщение."""
homework_name = homework.get('homework_name')
homework_status = homework.get('status')
if homework_status in VERDICTS.keys():
verdict = VERDICTS.get(homework_status)
if homework_name is None or homework_status is None:
raise KeyError('отсутствие ожидаемых ключей homework_name '
'и status в ответе API')
return f'Изменился статус проверки работы "{homework_name}". {verdict}'
def check_tokens() -> bool:
"""
Проверяет доступность переменных окружения.
Проверка токенов Практикума и
Bot API, id чата получателя. Возвращает булево значение.
"""
checker = all((PRACTICUM_TOKEN, TELEGRAM_TOKEN, TELEGRAM_CHAT_ID))
if not checker:
logging.critical('Отсутствует переменная токена!!!')
return checker
def main() -> None:
"""Основная логика работы бота."""
logger.info('Бот запущен')
if not check_tokens():
message = 'Отсутствует одна из переменных окружения'
logger.critical(message + '\nПрограмма остановлена.')
raise exception.MissingVariable(message)
try:
bot = telegram.Bot(token=TELEGRAM_TOKEN)
except telegram.error.InvalidToken as error:
message = f'Ошибка при создании бота: {error}'
logger.critical(message + '\nПрограмма остановлена.')
raise telegram.error.InvalidToken
current_timestamp = int(time.time())
last_homework = None
last_error = None
while True:
try:
response = get_api_answer(current_timestamp - TELEGRAM_RETRY_TIME)
homework = check_response(response)
if homework and homework != last_homework:
message = parse_status(homework[0])
send_message(bot, message)
last_homework = homework
else:
logger.debug('Статус домашней работы не изменился')
current_timestamp = response.get('current_date')
except Exception as error:
if str(error) != last_error:
message = f'Сбой в работе программы: {error}'
send_message(bot, message)
last_error = str(error)
finally:
time.sleep(TELEGRAM_RETRY_TIME)
if __name__ == '__main__':
try:
main()
except KeyboardInterrupt:
sys.exit()