Отправить результат SQL запроса через Telegram бота на Python. Обойти ограничение символов и привести в читаемый вид

Потребность:

  • Автоматизировать работу, которая привязывает меня к компу в выходные =). Необходимо знать актуальное состояние данных в БД

Задача:

  • Отправить в ТГ бота сообщение (предопределенное название селекта (СУБД - Postgres, Lib - psycopg2)) в ответ получить полный результат запроса

Описание сущностей: все таблицы, к которым будут обращаться селекты имеют вид:

  • Дата актуальности(10 символов, char) PK is not null
  • Код (2 символа, char) PK is not null
  • Числовое значение 1 (до 7 символов, int)
  • Числовое значение 2 (до 7 символов, int)
  • Числовое значение 3 (до 7 символов, int)
  • Таймстамп занесения данных в БД(19 символов, char) is not null

Проблемы с которыми столкнулся:

  1. Ограничение по количеству символов, так как необходим полный результат селекта (в таблице порядка 40к кортежей). Так как селект с фильтрацией, возвращается порядка двухсот строк, но и этого достаточно, чтобы словить ошибку из-за ограничения. Хочется узнать как бы обходить такую проблему сохраняя целостность кортежей (просто сплитить по ограничению кол-ва знаков не получится, плюс есть некоторые проблемы с отправкой цельного списка через send_message(отправляет только самый первый элемент (первую дату)), поэтому приходится генерировать целевое сообщение циклом for:

    cur.execute("select ... ")
    data = cur.fetchall() 
    string_to_send = ''
    hop_count = 0 #Сделал счетчик строк для информативности
    for tuple in data: #Курсор возвращает список тьюплов
        for string_to_append in tuple: 
            string_to_send += ' ' + str(none_case(string_to_append)) #Делаем из тьюпла строку. Функция none_case - преобразование None значений в строку "Null"
        string_to_send += '\n' #Переходим на новую строку для следующего кортежа
        hop_count +=1
    bot.send_message(message.from_user.id, text=f'Количество строк - {hop_count}') 
    bot.send_message(message.from_user.id, text=string_to_send) # Отправляю через два разных сообщения, чтобы увидеть кол-во строк до того, как свалится с ошибкой
    

)

  1. Нечитаемый формат данных. Пробовал разные варианты (табуляция и тд), но табличные данные, возвращаемые в виде множества строк все равно не особо воспринимаются глазом.

Возможно, решением обеих проблем будет автоматическая генерация статьи Telegraph (не уверен можно ли туда пихать таблицы, пока что не нагуглил, и возможность вообще ее генерировать автоматически тоже пока что вызывает сомнения :D)

Возможно, есть способ преобразовывать это все в эксель файл и отправлять через бота скрины данных в этой эксельке, чего, в принципе, будет минимально достаточно для закрытия потребности

Был бы очень рад услышать ваши варианты доработки того, что у меня сейчас существует, либо ваше видение решения задачи

Заранее извиняюсь за свой код, делаю это на питоне не так часто


Ответы (1 шт):

Автор решения: Violet

сплитить по ограничению кол-ва знаков

я просто оставлю это как пример:

if len(data) > 4096:
        for x in range(0, len(report), 4096):
            bot.send_message(message.chat.id, '{}'.format(report[x:x + 4096]))
    else:
        bot.send_message(message.chat.id, '{}'.format(report))

Нечитаемый формат данных

я использую для вывода небольших таблиц:

bot.send_message(message.chat.id, '`{}`'.format(data), parse_mode='Markdown')

автоматическая генерация статьи Telegraph

это возможно - https://telegra.ph/api и бот @telegraph

способ преобразовывать это все в эксель файл и отправлять через бота скрины данных в этой эксельке

потребуется

from oauth2client.service_account import ServiceAccountCredentials
import gspread

# Google
gscope = ['https://spreadsheets.google.com/feeds', 'https://www.googleapis.com/auth/drive']
gcredentials = 'ваш файл полученный по инструкции.json'
gdocument = 'название таблицы'

чтобы бот мог записывать в Таблицу:

Настройка интеграции с Google Drive, Google Spreadsheet
1. https://console.developers.google.com/
2. Библиотека – Создать проект
3. Поиск в библиотеке API:
   a. Google Drive – Включить
   b. Создать учётные данные
   c. Какой API вы используете – Google Drive API
   d. Откуда вы будете вызывать API – Веб-Сервер
   e. К каким данным вы будете обращаться – Данные приложения
   f. Используете этот API для App Engine или Compute Engine – Нет
   g. Выбрать тип учётных данных
   h. Роль – Проект – Редактор
   i. Тип ключа – JSON
   j. Продолжить (загрузка ключа в формате JSON)
4. Открыть полученный файл JSON
5. Скопировать значение поля "client_email"
6. https://docs.google.com/spreadsheets/
7. Создать новую таблицу
8. Настройки доступа
9. Ввести значение из п.5 и предоставить доступ на редактирование

Запись в Гугл Таблицу: дата, время, id, переменные

# Запись в Google Sheet Bot
def add_to_gsheet(message, data, text):
    credentials = ServiceAccountCredentials.from_json_keyfile_name(gcredentials, gscope)
    gc = gspread.authorize(credentials)
    wks = gc.open(gdocument).sheet1
    # noinspection PyBroadException
    try:
        # убрать если нужно чтобы была реакция при ЛС боту
        if message.chat.type == 'private':
            return
    except Exception:
        pass
    wks.append_row(
        [datetime.now().strftime('%d.%m.%Y %H:%M:%S'), 'Имя бота', message.from_user.id, text, '---', data])

вызываем например так:

add_to_gsheet(message, 'отмена проведения ' + number_check, 'чек')

таким образом в Таблицу будет записано следующее

06.04.2020 18:25:50 Имя бота 123 чек --- отмена проведения ММА00031234

Далее получим данные таблицы как pdf, преобразуем в jpg и отправим:

from pdf2image import convert_from_path
import requests
import os
from sys import platform
import fnmatch

if platform == "linux" or platform == "linux2":
    pass
    path = 'files/'
elif platform == "win32":
    poppler = 'PATH/poppler/bin'
    path = 'PATH/files/'

# Google Spreadsheet
# Param export GS
# &format=pdf                   //export format
# &size=a4                      //A3/A4/A5/B4/B5/letter/tabloid/legal/statement/executive/folio
# &portrait=false               //true= Potrait / false= Landscape
# &scale=1                      //1= Normal 100% / 2= Fit to width / 3= Fit to height / 4= Fit to Page
# &top_margin=0.00              //All four margins must be set!
# &bottom_margin=0.00           //All four margins must be set!
# &left_margin=0.00             //All four margins must be set!
# &right_margin=0.00            //All four margins must be set!
# &gridlines=false              //true/false
# &printnotes=false             //true/false
# &pageorder=2                  //1= Down, then over / 2= Over, then down
# &horizontal_alignment=CENTER  //LEFT/CENTER/RIGHT
# &vertical_alignment=TOP       //TOP/MIDDLE/BOTTOM
# &printtitle=false             //true/false
# &sheetnames=false             //true/false
# &fzr=false                    //true/false
# &fzc=false                    //true/false
# &attachment=false             //true/false
gs_id = '1QJRxp...gVVBIw'
gs_url = 'https://docs.google.com/spreadsheets/d/' + gs_id + '/export?format=pdf&gid='
gs_param = '&size=a5&portrait=false&vertical_alignment=MIDDLE&horizontal_alignment=CENTER'
gs_table = [581871535, 1884134963, 1426883343]  # gid


def main(gid):
    # Download Google Spreadsheet
    response = requests.get(gs_url + str(gid) + gs_param)
    with open(path + 'test-table.pdf', 'wb') as out_file:
        for chunk in response:
            out_file.write(chunk)

    # Google Spreadsheet pdf to jpg
    convert_from_path(path + 'test-table.pdf', poppler_path=poppler, output_folder=path,
                      output_file='test-table', fmt='jpg')

    # Send Google spreadsheet jpg send from bot
    requests.post('https://api.telegram.org/bot' + bot + '/sendPhoto',
                  files={'photo': open(path + 'test-table0001-1.jpg', 'rb')},
                  data={'chat_id': 'id_tlgrm'})


for gid in gs_table:
    main(gid)

os.remove(fnmatch.filter(os.listdir('files'), '*.pdf'))
os.remove(fnmatch.filter(os.listdir('files'), '*.jpg'))

gs_param подбираются индивидуально

→ Ссылка