Pull to refresh

Использование Python для формирования отчетов в отдельно взятой компании

Reading time5 min
Views15K
Эта история случилась в реальной компании, несмотря на то, что некоторые имена и события вымышлены.

Слава был рядовым разработчиком в небольшой фирме в городе N. Фирма занималась предоставлением услуг образовательным организациям. В наличии было несколько приложений, которые необходимо поддерживать, дорабатывая помаленьку, понемногу. Вот только начальство Славы не верило в его усилия и то, что он ест свой хлеб не просто так. Кроме того, начальство в информационных технологиях не так чтобы очень, но хотело понимать, что делают сотрудники и какая продуктивность у отдела продаж (который надо сказать состоял из одного с половиной человека).

Хитрым глазом смотрело начальство на менеджера по продажам и вопрошало: а что ты сделал сегодня для Родины. Продавец отвечал: провел столько то встреч, предлагал услуги стольким то людям. Руководство пришло к Славе и говорит: скажи, правду ли говорят сотрудники или так, отсебятину несут. Нужна статистика.

Состоялся диалог:

— Пользователи оплачивают услугу через сторонний сервис для приема онлайн платежей? Так?
— Так.
— Доступ к данному сервису у руководства компании есть?
— Есть.
— Так, наверное, там и выгрузка по платежам есть.
— Есть.
— Но нужно, чтобы был еще дополнительный отчет?
— Нужен.
— Почему?
— Потому что так удобнее, потому что ты должен, потому что “так и так”.

Справедливости ради надо сказать, что нужны были дополнительные данные.

Не долго думая Слава исполнил простенький запрос с последующим выбором в Excel.

Отчет представлял из себя набор данных в один столбец на одной странице. По началу к Славе подходили в хаотичном порядке, посередине дня и спрашивали. А вот дай ка ты отчет по этой организации, а вот теперь по этой. Не долго думая Слава решил, что хватит это терпеть и нужно делать что-то прекрасное и унифицированное, которое удовлетворяло потребности запрашивающих.

Возник небольшой скрипт, который позволял делать рассылку заинтересованным персоналиям:

import openpyxl, pymysql, os
from smtplib import SMTP_SSL
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email import encoders
import datetime

# Excel Settings
today = datetime.date.today().strftime('%d.%m.%Y')
excel_file = 'Oplata_polzovateley_' + today + '.xlsx'

# SQL settings
host = ''
user = ''
passwd = ''
db = ''
port=0000
headers = ['id', 'email', 'Контрагент', 'Тариф', 'дата оплаты', 'дата окончания', 'Группа', 'Кол-во входов', 'тип оплаты']

# SMTP Mail settings
smtp_server = 'smtp.gmail.com'
mail_login = ''
mail_passwd = ''
receiver = ['']
cc = ['']


def main():
   # Fetch Data from SQL server
   conn = pymysql.connect(host=host, user=user, passwd=passwd, db=db, port=port)
   cursor = conn.cursor()
   cursor.execute('''select * from table''')
   data = cursor.fetchall()
   conn.close()

   # Write Data to Excel file
   wb = openpyxl.Workbook()
   contractors = {}
   for item in data:
       diff = item[6] - item[5]

       item = list(item)
       # print(item)
       if diff.days > 10:
           item.append('полный')
       else:
           item.append('триальный')

       item = tuple(item)

       if item[0][:30] in contractors:
           contractors[item[0][:30]] += 1
       else:
           wb.create_sheet(item[0][:30])
           contractors[item[0][:30]] = 2
           for i in range(1, len(headers) + 1):
               letter = openpyxl.utils.get_column_letter(i)
               wb[item[0][:30]][letter + '1'] = headers[i - 1]
       wb[item[0][:30]]['A' + str(contractors[item[0][:30]])] = contractors[item[0][:30]] - 1
       for i in range(2, len(headers) + 1):
           letter = openpyxl.utils.get_column_letter(i)
           wb[item[0][:30]][letter + str(contractors[item[0][:30]])] = item[i]
   wb.save(excel_file)
   wb.remove(wb['Sheet'])
   wb.save(excel_file)

   # Compose attachment
   part = MIMEBase('application', "octet-stream")
   part.set_payload(open(excel_file, "rb").read())
   encoders.encode_base64(part)
   part.add_header('Content-Disposition', 'attachment; filename="%s"' % os.path.basename(excel_file))

   # Compose message
   msg = MIMEMultipart()
   msg['From'] = mail_login
   msg['To'] = ', '.join(receiver)
   msg['Cc'] = ', '.join(cc)
   msg['Subject'] = excel_file
   msg.attach(part)

   # Send mail
   tosend = receiver + cc
   smtp = SMTP_SSL('smtp.gmail.com')
   smtp.connect(smtp_server)
   smtp.login(mail_login, mail_passwd)
   smtp.sendmail(mail_login, tosend, msg.as_string())
   smtp.quit()

   # Wipe file
   os.remove(excel_file)


if __name__ == '__main__':
   main()

Сделано, конечно, не очень подумал Слава. Но как говорится: “А, и так сойдет”.

Каждое утро Слава приходил на работу, запускал скрипт и отправлял письмо на почту заинтересованным лицам.
Следующим этапом стала настройка автоматической отправки. Работало это примерно так:

docker build --tag=reports.
docker run -it --rm reports

И прописью в кроне:
0 8 * * * docker run --rm foo

Каждый день в 8 утра письмо уходило. Руководство было довольно и даже начало думать, что Слава каждый день, ровно в 8 утра приходил на работу, составлял отчет в Excel, а затем руками отправлял на почту. Причем делал это и в субботу, и в воскресенье, и в снег, и в дождь, и в полярную ночь.

GitHub

До поры, до времени всё было нормально и руководство терпело, но недолго. Однажды состоялась встреча, на которой объявили новый приказ. Слушайте, слушайте и не говорите, что не слышали. Сим объявляю, что каждый сотрудник, каждый день должен отчитываться вышестоящему по званию, а то еще вышестоящему. Ну в общем вы поняли.

— Каждый день?, — спросил Слава.
— Каждый день. — ответил начальник технического отдела.
— А может быть не надо?
— Надо Слава, надо.

У Славы возникло несколько вопросов по данной ситуации. Непосредственной обязанностью была доработка программного обеспечения и его отчеты должны состоять из чего-то вроде этого:
feat(Module) Fixed a bug in NoteLineCount… not seriously…
upd(Module2) Pay no attention to the man behind the curtain
fixed(Module3) I was told to leave it alone, but I have this thing called OCD, you see

* Примечание: название сообщений к коммитам взяты отсюда

Слава решил, что будет составлять отчет на их основе. Прошла неделя. Состоялся новый разговор примерно такого содержания:

— Вот вы присылаете отчеты каждый день, но прогресса не видно. Что к примеру такое: “добавлена маска при вводе номера телефона на фронт-энде”.
— Ну, вот смотрите. Раньше приходилось вводить свой номер телефона и сверять количество циферок и было неудобно. А вот так — удобно.
— Хм. Хорошо. Понятно. А как вы объясните вот это: “Рефакторинг кода для модуля Контрагенты. Функция для валидации вынесена в отдельный метод”.
— Ну, понимаете. Есть такая штука — система контроля версий, в которой указывается кто, когда и что делал. Вот смотрите. Здесь коммиты, а здесь код, который был изменен. Вот код добавлен, вот код удален.
— Хорошо. Давайте тогда так. Делайте отчет на основании этих ваших коммитов, а мы потом будем собираться и сверять как да что вы там написали.

В итоге — получилось вот что:

GitHub

Скрипт брал изменения в репозитории и отправлял письмецо о том, что было сделано, а что нет.

Ранее Слава постеснялся сообщить о том, что те кто будут проверять ничегошеньки не понимают в написании кода. Может быть, руководство смыслит в продажах, покупках и прочей всякой всячине и платит деньги, но видеть будет определенные строчки кода на не очень знакомом языке, наблюдать как мерцают зеленые и красные строчки при переходе с коммита на коммит.

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

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

GitHub

Оставалось сделать так, чтобы формировался фейковый код с последующими коммитами. Нужно ли это делать? Время покажет.

P.S.: На этом история Славы не закончилась. Было еще несколько событий, которые повлияли на его судьбу, но это совсем другая история. Интересно почитать в комментариях схожие истории из жизни и решения, которые создавались на их основе. Вполне возможно, что проект в последующем будет дорабатываться на основе данных комментариев.
Tags:
Hubs:
Total votes 9: ↑8 and ↓1+7
Comments3

Articles