Финальный проект: ежедневный автоматический отчёт
Пора собрать всё вместе. Спроектируем реальный проект автоматизации — ежедневный отчёт о продажах — от чтения данных до рассылки по расписанию с логами и обработкой ошибок.
Суть: настоящий проект — это пайплайн из функций (читать → считать → оформить → отправить), запускаемый по cron, с логированием и try/except на каждом ответственном шаге.
Весь курс вёл к этому моменту. Возьмём задачу, ради которой автоматизацию обычно и затевают: каждое утро собирать отчёт о вчерашних продажах и рассылать руководству. В ней сходятся все темы: файлы, таблицы, форматирование, email, расписание, надёжность. Сначала — архитектура: разложим проект на функции-блоки пайплайна.
АРХИТЕКТУРА ПРОЕКТА
cron 09:00
|
main() --try/except + logging--
|
read_sales() -> список сделок (csv)
|
build_report() -> агрегаты по менеджерам
|
to_excel() -> отчёт.xlsx (openpyxl)
|
send_email() -> руководству (smtplib)
|
log 'успех/сбой'Главное в проекте — что main лишь оркестрирует вызовы, а каждая функция делает одну вещь. Такую структуру легко тестировать по частям и чинить. Ядро отчёта — расчёт и проверка — целиком на stdlib, запустим его в браузере как сердце проекта.
Попробуй сам ▶
import logging
from collections import defaultdict
logging.basicConfig(level=logging.INFO, format='%(levelname)s: %(message)s')
def read_sales():
# в реальности — csv.DictReader из файла
return [('Аня', 75000), ('Боря', 1200), ('Аня', 18000),
('Вика', 82000), ('Боря', 3500)]
def build_report(sales):
if not sales:
raise ValueError('нет данных за день')
by_mgr = defaultdict(int)
for mgr, amount in sales:
by_mgr[mgr] += amount
return dict(by_mgr)
def main():
try:
sales = read_sales()
report = build_report(sales)
total = sum(report.values())
logging.info(f'Отчёт собран: {len(report)} менеджеров, {total} руб')
for mgr, amt in sorted(report.items(), key=lambda x: -x[1]):
print(f' {mgr:6} {amt:>8,} руб')
print(f' {"ИТОГО":6} {total:>8,} руб')
logging.info('Готово к рассылке')
except Exception as e:
logging.error(f'Сбой пайплайна: {e}')
main()Обратите внимание, как собрались навыки: defaultdict для агрегации, форматирование колонок, logging на каждом шаге, try/except вокруг всего пайплайна и проверка «нет данных». Шаги, трогающие диск и сеть (Excel и email), показаны как врезка.
import logging
from pathlib import Path
def to_excel(report, path):
from openpyxl import Workbook
wb = Workbook(); ws = wb.active
ws.append(['Менеджер', 'Выручка'])
for mgr, amt in report.items():
ws.append([mgr, amt])
wb.save(path)
return path
def send_email(path):
import os, smtplib
from email.message import EmailMessage
msg = EmailMessage()
msg['From'] = '[email protected]'; msg['To'] = '[email protected]'
msg['Subject'] = 'Дневной отчёт'
msg.set_content('Отчёт во вложении.')
msg.add_attachment(Path(path).read_bytes(),
maintype='application',
subtype='vnd.openxmlformats-officedocument.spreadsheetml.sheet',
filename='report.xlsx')
with smtplib.SMTP_SSL('smtp.company.ru', 465) as s:
s.login('[email protected]', os.environ['EMAIL_PASSWORD'])
s.send_message(msg)
logging.info('Отчёт отправлен')А запускается всё это строкой cron — ежедневно в 9 утра, с логом в файл.
# crontab -e
0 9 * * * /srv/report/.venv/bin/python /srv/report/main.py >> /srv/report/run.log 2>&1Завершённый проект полезно дополнить парой защитных механизмов, отличающих учебный скрипт от рабочего инструмента. Первый — конфигурация отдельно от кода: адреса почты, пути и пороги выносят в отдельный файл настроек или переменные окружения, чтобы менять поведение без правки логики. Второй — самопроверка результата перед отправкой: прежде чем разослать отчёт, скрипт убеждается, что итоговая сумма правдоподобна и файл не пуст, иначе лучше поднять тревогу, чем разослать ерунду руководству. Третий — понятный путь восстановления: если запуск сорвался, по логам должно быть видно, на каком шаге, чтобы повторить только его. Эти три привычки и превращают набор функций в инструмент, которому доверяют.
Как работает под капотом
Проект собран по принципу разделения ответственности: каждая функция знает только свою задачу и общается с остальными через возвращаемые данные. main — дирижёр: он вызывает функции по порядку и ловит любые ошибки одним внешним try/except. Если упадёт чтение — не отправится битый отчёт; если упадёт отправка — это попадёт в лог, и вы об этом узнаете.
Запись >> run.log 2>&1 в cron перенаправляет и обычный вывод, и ошибки в файл лога. Так даже падение до инициализации logging (например, синтаксическая ошибка) будет зафиксировано. Это последний рубеж наблюдаемости — без него ночные сбои остаются невидимыми.
Частые ошибки
- Складывать всё в одну функцию. Монолитный main невозможно тестировать и чинить по частям.
- Отправлять отчёт до проверки данных. Сначала убедитесь, что данные есть и корректны, потом рассылайте.
- Забыть про логи cron. Без перенаправления вывода в файл ночное падение не оставит следов.
Best practices
- Стройте проект как пайплайн из маленьких функций; main только оркестрирует.
- Тестируйте логику (расчёт) отдельно от побочных эффектов (файлы, сеть).
- Логируйте каждый этап и перенаправляйте вывод cron в файл — это ваши глаза в проде.
Итоги. Настоящий проект автоматизации — это аккуратный пайплайн из функций, запускаемый по расписанию, с логами и обработкой ошибок на каждом шаге. Вы прошли весь путь: от «зачем» до работающей службы, которая трудится за вас каждое утро. Теперь у вас есть и навыки, и архитектурное мышление, чтобы автоматизировать собственную рутину.