Рассмотрим устройство урока по Trello.
В папке trello
есть два файла class_Trello.py
и trello_for_flask.py
, а также каталог creds
.
Поговорим про каждый по отдельности.
В данной папке находится файл trello_tokens.py
, содержащий сервисные данные, а именно token
и api key
сервисного аккаунта и callback_url
содержащий адрес сервера, на котором развернуто приложение. Все ниже описанные функции выполняют действия от имени сервисного аккаунта, например создают на нём доски, приглашают от его имени пользователей и т.д.
В этом файле находятся базовые, сервисные и сложносоставные функции для взаимодействия приложения и платформы. Важно понимать, что, так как у Trello нет собственной библиотеки API, все взаимодействие с данным сервисом будет производиться с помощю библиотеки requests
, отправляя запросы к специальному url, на котором работает сервис API.
Разберем, что делают выше описанные функции:
Каждая базовая функция возвращает ответ на API запрос с сервисной информаций, а также ошибку в случае, если что-то пошло не так.
Функция | Аргументы | Описание |
---|---|---|
create_board(board_name) | название доски | Создает для аккаунта доску с переданным в функцию названием |
create_board_with_template(board_name, idOrganization) | название доски, название команды | Создает доску по шаблону (используется шаблон проектной доски) с переданным в функцию названием, при этом определяя доску в команду, название которой также передаёется в функцию |
Для понимания: для каждого пользователя создается уникальное окружение в собственной команде, где у доски уникальное название формата Training board for <email>
.
delete_board(board_id) | id доски | Удаляет доску на сервисном аккаунте с переданным id |
add_user_to_group(user_id, group_id) | id пользователя и id группы | Добавляет пользователя с user_id в группу с group_id. Возвращает id группы |
create_list(board_id, list_name) | id доски и навзание списка | Создает в доске с переданным id список с указанным названием. Данная функция возвращает id списка |
create_card(list_id, card_name) | id списка и навзание карточки | Создает в списке с переданным id карточку с указанным названием. Возвращает id карточки |
invite_member_to_board(board_id, member_email) | id доски и электронная почта | Отправляет на указанный email ссылку с приглашением на доску с переданным id |
create_webhook(callback_url, id_model) | адрес получателя вебхуков и id объекта | Создает вебхук с указанным url получателя обновлений для указанного объекта |
list_of_webhooks(user) | user id | Возвращает список вебхуков для пользователя с переданным id |
delete_webhook(webhook_id) | id ввебхука | Удаляет вебхук с указанным id в сервисном аккаунте |
create_organization(displayName) | название команды | Создает в сервисном аккаунте команду с указанным названием |
delete_organization(idOrganization) | id команды | Удаляет в сервисном аккаунте команду с указанным названием |
Функция | Аргументы | Описание |
---|---|---|
is_user(email) | почта пользователя | Проверяет, начал ли пользователь урок посредством поиска записи с указанным email в базе данных. Возвращает True - если пользозватель есть, False - если пользователя нет и Error - если больше одного пользователя в базе данных |
calculate_score(marks_list) | json объект с оценками и весами заданий | Рассчитывает итоговую оценку пользователя и возвращает её |
На вход функция получает электронную почту пользователя и название урока (например trello1 - первый урок по платформе Trello).
Данная функция является ключевой при инициализации урока, так как создает индивидуальное окружение.
Далее разберем работу функции по-порядку:
is_user
;callback_url
и google_sheet
, которые содержат соостевтственно адрес для вебхуков и сервисную таблицу с сообщениями чат-бота, сценарием и шаблоном json-объекта с оценками;create_organization
и запысывает это название в сервисную переменную organization_name
;create_board_with_template
с названием в формате Training board for <email>
в команде с названием из прошлого пункта;invite_member_to_board
;create_webhook
и проверяет выполнение этого шага с помощью конструкции try - except;email
, board_id
, organization_name
, webhook_id
, stage
, marks_list
) в дополнительной таблице бд;Результатами работы функции являются создание индивидуального окружения, приглашение в него пользователя, настройка вебхука, запись в таблицу платформы в бд и отправление пользователю стартового сообщения.
На вход функция получает электронную почту пользователя.
Данная функция завершает цикл выполнения пользователем урока, удаляет индивидуальное окружение и сервисные данные.
Далее разберем работу функции по-порядку:
webhook_id
, organization_id
, board_id
) пользователя из бд, с помощью поиска записи по почте;delete_webhook
, чтобы при остальных действиях с доской не приходили реквесты серверу;delete_board
и delete_organization
соответственно;Результатом работы функции являются удаленные индивидуальное окружение и записи из базы данных.
На вход функция получает электронную почту пользователя.
Данная функция используется при отправке боту команды /restart
. Она подсчитывает оценку пользователя, отправляет её через чат-бота и выставляет в классрум.
Далее разберем работу функции по-порядку:
calculate_score
;send_message
;send_score
.Результатом работы функции является подсчитанная промежуточная оценка при перезапуске урока, выставленная в классрум и отправленная пользователю в чате.
Эти функции находятся в классе Trello
, импортировать их можно так:
from Trello.class_Trello import Trello
.
Кроме этих функций в классе Trello в файле вы найдёте функции
send_message
иsend_score
. Эти функции импортируются из других файлов на сервере и используются для отправки сообщения пользователю через чат бота и выставления оценки в классрум соответственно.
Это основной файл урока. В нём находится блюпринт для фласка, содержащий сценарий проверки с выбором кейса в зависимости от этапа пользователя. Данный скрипт активируется при получении реквеста от вебхука.
Блюпринт содержит один роут, специально для взаимодействия по урокам Trello, реализованные отдельно через два метода.
GET
используется для ответа на первый (активационный) вебхук запрос;Post
активируется при остальных запросах вебхука, то есть для обработки шага пользователя и завершения задания.Далее разберем подробнее функцию callback - основной скрипт файла:
В начале функция получает json - объект из тела запроса. Здесь содержится основная информация по действию пользователя. Шаблон запроса от вебхука по "удачному" действию пользователя будет приведен после описания файла.
# catching json object from webhook
json_content = request.get_json(force=True)
Так как в запросе отсутствует почта пользователя, поиск в базе данных будет производится по id доски. Его мы получаем из json - объекта.
# catching board id from webhook to identify user
board_id = json_content["model"]["id"]
Далее из найденной в бд записи функция получает сервисные данные и записывает их в переменные (email
, stage
, marks_list
).
У Trello есть одна проблема: вебхуки про назначение нового дедлайна карточки могут прилетать несколько раз. Для проверки этих запросов и для того чтобы не "беспокоить" основной блок функции без надобности используется следующий блок кода:
try:
if json_content["action"]["data"]["old"]["dueReminder"] == None:
if stage != 4:
return "200 Trello DueReminder extra webhook"
except:
pass
try:
if json_content["action"]["data"]["old"]["due"] == None:
if stage != 4:
return "200 Trello Due extra webhook"
except:
pass
Так как именно на 4 пункте пользователя просят назначить карточке срок, функция проверяет текущий этап пользователя и присутствие полей dueReminder
и due
в json - объекте. Если эти условия не выполняются - скрипт преджевременно прекращает выполнение кода.
Далее, при начале выполнения основного блока функции, запрос проходит через ветвдение, которое ориентируется на текущий этап пользователя. Для примера ниже приведен пример для проверки первого пункта:
if stage == 1:
# проверка действия
if json_content["action"]["type"] == google_sheet[stage][6]:
mark = 5
# проверка списка в котором создана карточка
if json_content["action"]["data"]["list"]["name"] == google_sheet[stage][4]:
mark = 7.5
# проверка названия карточки
if json_content["action"]["data"]["card"]["name"].lower() == google_sheet[stage][5]:
mark = 10
else:
logger.error("Здесь справочные данные")
return send_message(email, google_sheet[stage][2])
Можно заметить, что оценка варьируется в зависимости от того, насколько правильно выполнил задание пользователь.
Также при неверном выполнении задания ошибка логгируется и пользователю посылается специальный комментарий с подсказкой.
В конце выполнения итерации для 1 - 7 пунктов происходит следующее:
Отдельно стоит рассмотреть 8 итерацию. Начало такое же как и у 1-7, то есть проверка и оценка действий пользователя.
Далее происходит обновление переменной с оценками, подсчет итоговой оценки с помощью функции calculate_score
. После этого пользователю отправляется сообщение с оценкой и подробно расписанными баллами по пунктам (пример ниже), а также выставляется оценка в классрум с помощью функции send_score
. Завершается выполнение скрипта запуском функции delete_environment
, которая удаляет индивидуальное окружение и запись из бд.
Я проверил твое задание. Ты набрал 7.19 баллов из 10.
Создание карточки: 7.5/10.
Описание: 10/10.
Назначение исполнителя: 10/10.
Назначение дедлайна: 10/10.
Установка метки: 10/10.
Добавление коментария: 0/10.
Добавление вложения: 0/10.
Перемещение между списками: 10/10.
(Позаимствован с официального сайта API Trello https://developer.atlassian.com/cloud/trello/guides/rest-api/webhooks/)
{
"action": {
"id": "51f9424bcd6e040f3c002412",
"idMemberCreator": "4fc78a59a885233f4b349bd9",
"data": {
"board": {
"name": "Trello Development",
"id": "4d5ea62fd76aa1136000000c"
},
"card": {
"idShort": 1458,
"name": "Webhooks",
"id": "51a79e72dbb7e23c7c003778"
},
"voted": true
},
"type": "voteOnCard",
"date": "2013-07-31T16:58:51.949Z",
"memberCreator": {
"id": "4fc78a59a885233f4b349bd9",
"avatarHash": "2da34d23b5f1ac1a20e2a01157bfa9fe",
"fullName": "Doug Patti",
"initials": "DP",
"username": "doug"
}
},
"model": {
"id": "4d5ea62fd76aa1136000000c",
"name": "Trello Development",
"desc": "Trello board used by the Trello team to track work on Trello. How meta!\n\nThe development of the Trello API is being tracked at https://trello.com/api\n\nThe development of Trello Mobile applications is being tracked at https://trello.com/mobile",
"closed": false,
"idOrganization": "4e1452614e4b8698470000e0",
"pinned": true,
"url": "https://trello.com/b/nC8QJJoZ/trello-development",
"prefs": {
"permissionLevel": "public",
"voting": "public",
"comments": "public",
"invitations": "members",
"selfJoin": false,
"cardCovers": true,
"canBePublic": false,
"canBeOrg": false,
"canBePrivate": false,
"canInvite": true
},
"labelNames": {
"yellow": "Infrastructure",
"red": "Bug",
"purple": "Repro'd",
"orange": "Feature",
"green": "Mobile",
"blue": "Verified"
}
}
}
Разберёмся с отправкой оценок. Для этого используется форматирование строк. В таблице присутствует заранее подготовленный шаблон текста, в который с помощью python передаются оценки:
Я проверил твое задание. Ты набрал {:.2f} баллов из 10.
Создание карточки: {}/10.
Описание: {}/10.
Назначение исполнителя: {}/10.
Назначение дедлайна: {}/10.
Установка метки: {}/10.
Добавление коментария: {}/10.
Добавление вложения: {}/10.
Перемещение между списками: {}/10.
Выставляю твою оценку в Google Classrom
Эти строки расположены в файле GSheetsApi.py
. Они передают данные о гугл-таблице, куда переносятся оценки студентов.
if os.path.exists('core/google_api/data/token.pickle'):
with open('core/google_api/data/token.pickle', 'rb') as token:
creds = pickle.load(token)
service = build('sheets', 'v4', credentials=creds).spreadsheets().values()
RANGE_GRADES = 'grades'
В файле trello_for_flask.py содержится сама функция, которая записывает оценки пользователей телеграмм бота и их емайл.
grades = {'values': [[email, marks_list["1"]["mark"], marks_list["2"]["mark"], marks_list["3"]["mark"],
marks_list["4"]["mark"], marks_list["5"]["mark"], marks_list["6"]["mark"],marks_list["7"]["mark"], marks_list["8"]["mark"]]]}
service.append(spreadsheetId=SPREADSHEET_ID,
range=RANGE_GRADES,
valueInputOption='USER_ENTERED',
body=grades).execute()