От кода до сертификата: Создание веб-приложения на Python Flask для автоматизации выдачи сертификатов26.04.2024 16:30
Привет всем! Меня зовут Дмитрий. Я надеюсь, что статья будет полезной и интересной для вас (не пинайте сильно, первый опыт, мысли путаются). Тема моей статьи — создание веб-приложения на Python Flask для автоматизации выдачи сертификатов и вдохновился написанием ее после прочтения Почта без хлопот: автоматизация отправки писем с помощью Python
В современном мире всё больше процессов автоматизируется, чтобы упростить жизнь людей и сократить время на выполнение рутинных задач. Одной из таких задач является выдача сертификатов. Раньше в организации где я работаю они выдавались в бумажном виде, но с приходом COVID-19 когда ушли на дистант, и все курсы были дистанционно я решил написать сначала скрипт, а потом и полноценное приложение с веб интерфейсом для рассылки сертификатов.
В этой статье я расскажу о процессе создания веб-приложения на Python/Flask, которое будет использоваться для автоматизации процесса выдачи сертификатов. Расписывать все основы я не буду поэтому перейдем с разу к делу, походу буду комментировать куски кода.
Выглядит она так:
можно обойтись и без радиокнопки «Свидетельство», но тк я писал с перспективой на то что возможно будет выдача и удостоверений она тут осталась, текст сообщения вводится если есть какие-то дополнительные сведения он попадёт в шаблон письма о нем будет дальше.
На вход я получаю таблицу от куратора курса которая содержит столбцы:
Наименование мероприятия
Дата проведения
Объём программы
Фамилия
Имя
Отчество
E‑mail
Дата выдачи
Статус обучения
Куратор ФИО
Куратор ТЕЛ
Куратор E‑MAIL
Файл приложения app.py
import os
from flask import Flask, request, render_template
from parse_table import table_courses
from html2pdf import table_cert, tamplate_html
from send_mail import collection_info
UPLOAD_FOLDER = 'upload/'
app = Flask(__name__)
app.config['FILE_UPLOADS'] = UPLOAD_FOLDER
@app.route('/certificate', methods=['GET', 'POST'])
def cert():
if request.method == 'POST':
# Шаблон письма
template_email = 'templates/sample_mail/sample_edu_doc.html'
# Тема письма
subject_email = request.form['subject_email']
# Текст письма
text_email = request.form['text_email']
# таблица с ФИО и адресами
table_email = request.files['table_email']
table_email.save(os.path.join(app.config['FILE_UPLOADS'], table_email.filename))
list_email = table_cert(table_email)
# print(list_email)
cert_list = tamplate_html(list_email)
ok_list, err_list = collection_info(template_email,
subject_email,
list_email=list_email,
file_name=cert_list)
return render_template('list_spam.html', ok_list=ok_list, err_list=err_list)
else:
return render_template('dokuments.html')
if __name__ == '__main__':
app.run()
В функции cert описана логика передачи данных на отправку, шаблон письма, тему, текст и таблицу, дальше таблица обрабатывается в файле html2pdf.py
В функции table_cert таблица с помощью pandas преобразуется в словарь состоящий из кортежей включающих в себя данные слушателя и название файла сертификата.
В функции html2pdf задаются параметры будущего сертификата: размер, отступы, ориентация, указываются пути до исполняемого файла (он нужен для работы библиотеки pdfkit) и путь к CSS файлу в котором указаны настройки шаблона удостоверения.
В функции image_file_path_to_base64_string картинки конвертируются тк без этого оно не работает.
В функции tamplate_html формируется html шаблон сертификата и конвертируется в .pdf записывая все это в список.
Когда все списки готовы, объявляются две переменные ok_list, err_list они послужат при выводе конечного результата в веб интерфейсе в две колонки успешно отправленные и отправленные с ошибкой, и данные собранные выше передаются на отправку в функцию collection_info.
import re
import os
from config import PORT, SERVER, LOGIN, PWD
import smtplib
from email.mime.application import MIMEApplication
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email import encoders
from email.header import Header
from email.utils import formataddr
from bs4 import BeautifulSoup
from jinja2 import Template
def collection_info(template, subject, list_email, **params):
ok_list, err_list = [], []
for email in list_email:
ok, error = send_email(template,
subject,
email.get('email'),
first_name=email.get('name'),
second_name=email.get('second_name'),
cert_name=email.get('cert_file'),
kyrator=email.get('kyrator'),
kyrator_tel=email.get('kyrator_tel'),
kyrator_email=email.get('kyrator_email'),
**params)
ok_list.append(ok)
err_list.append(error)
return ok_list, err_list
def send_email(template, subject, email, **params):
server = smtplib.SMTP_SSL(SERVER, PORT)
server.login(LOGIN, PWD)
print(f'Переменная email: {email}')
print(f'Переменная params: {params}')
msg = MIMEMultipart()
msg['From'] = LOGIN
msg['To'] = email
msg['Subject'] = Header(subject, 'utf-8')
html = open(template, encoding='utf-8').read()
template_html = Template(html).render(subject_email=subject,
first_name=params.get('first_name'),
second_name=params.get('second_name'),
text_email=params.get('text_email'),
link_form=params.get('link_form'),
link_teacher=params.get('link_teacher'),
kyrator=params.get('kyrator'),
kyrator_tel=params.get('kyrator_tel'),
kyrator_email=params.get('kyrator_email'),
)
body = BeautifulSoup(template_html, 'html.parser')
msg.attach(MIMEText(body, 'html', 'utf-8'))
# Проверяем если вложение есть до прикрепляем к письму
name_attachment = params.get('file_email')
cert_attachment = params.get('cert_name')
if name_attachment:
file_attachment = os.path.basename(name_attachment.filename)
open_attach = MIMEApplication(open('upload/'+name_attachment.filename, 'rb').read())
encoders.encode_base64(open_attach)
open_attach.add_header('Content-Disposition', 'attachment', filename=file_attachment)
msg.attach(open_attach)
elif cert_attachment:
print(cert_attachment)
open_attach = MIMEApplication(open(cert_attachment, 'rb').read())
encoders.encode_base64(open_attach)
open_attach.add_header('Content-Disposition', 'attachment', filename=cert_attachment)
msg.attach(open_attach)
ok, error = [],[]
try:
server.sendmail(LOGIN, msg['To'], msg.as_string())
server.quit()
ok = f"{msg['To']} - OK"
except BaseException as err:
e = re.search('\(([^)]+)', str(err)).group(1)
server.quit()
error = f"{msg['To']} - {e}"
return ok, error
Из полученных данных формируются html шаблоны и рассылаются по адресам с настроенной почты, и создаются два списка которые в последствии выведутся на экран в колонки успешной или неудачной отправки.
Шаблон письма
код под спойлером
Hidden text
Уважаемый(ая) {{ first_name }} {{ second_name }}!
Направляем {{ subject_email }}.
{% if text_email != None %}
{{ text_email }}
{% else %}
{% endif %}
Если у вас остались вопросы, обращайтесь к куратору программы.