[Python] Créez un linebot pour écrire le nom et l'âge sur l'image

Ce que j'ai fait

Amélioration du [robot de ligne qui dessine une date arbitraire sur une photo] précédemment créé (https://qiita.com/satoshi_199188/items/4d9bbe38ef011af5b662), enregistre le nom et la date de naissance dans la base de données et son âge à partir de la date de prise de vue J'ai créé un robot de ligne qui calcule s'il s'agit d'une photo ou l'écrit sur une image. Cliquez ici pour github

・ Assistant record de croissance (ID: @ 033cynwe) スクリーンショット 2020-05-05 17.14.38.png IMG_20200513_101654.png IMG_20200513_101617.png

environnement

Ce qu'il ne faut pas écrire dans cet article

--Création de canaux LineBot --Déployer sur heroku

Texte intégral

main.py

main.py


from flask import Flask, request, abort
from linebot import LineBotApi, WebhookHandler
from linebot.exceptions import InvalidSignatureError
from linebot.models import (FollowEvent, PostbackEvent, TemplateSendMessage, MessageAction,\
                                            ButtonsTemplate, DatetimePickerTemplateAction, ImageMessage, \
                                            ImageSendMessage, MessageEvent, TextMessage, TextSendMessage)
from pathlib import Path
from PIL import Image, ImageDraw, ImageFont, ImageFilter
import datetime
import os

import database

app = Flask(__name__)
app.debug = False

#Obtenir des variables d'environnement
YOUR_CHANNEL_ACCESS_TOKEN = os.environ["YOUR_CHANNEL_ACCESS_TOKEN"]
YOUR_CHANNEL_SECRET = os.environ["YOUR_CHANNEL_SECRET"]

line_bot_api = LineBotApi(YOUR_CHANNEL_ACCESS_TOKEN)
handler = WebhookHandler(YOUR_CHANNEL_SECRET)

#Chemin de référence de l'image
SRC_IMAGE_PATH = "static/images/{}.jpg "
MAIN_IMAGE_PATH = "static/images/{}_main.jpg "
PREVIEW_IMAGE_PATH = "static/images/{}_preview.jpg "

@app.route("/callback", methods=['POST'])
def callback():
    signature = request.headers['X-Line-Signature']
    body = request.get_data(as_text=True)
    app.logger.info("Request body: " + body)

    try:
        handler.handle(body, signature)
    except InvalidSignatureError:
        abort(400)

    return 'OK'

#Suivez l'événement
@handler.add(FollowEvent)
def handle_follow(event):
    line_bot_api.reply_message(
        event.reply_token,
        TextSendMessage(text=
        "Merci de vous être inscrit comme ami. J'écrirai dans l'image l'âge de la personne sur la photo. Veuillez m'envoyer une image."))

#Recevoir des images
@handler.add(MessageEvent, message=ImageMessage)
def handle_image(event):
    global message_id, user_id, name_list, user_dict, num, src_image_path
    
    #Liste de stockage des informations utilisateur
    name_list = []
    day_list = []
    user_dict = {}
    #message_Obtenir l'identifiant
    message_id = event.message.id
    #user_Obtenir l'identifiant
    user_id = event.source.user_id
    
    #Message de nom de fichier_Chemin converti en identifiant
    src_image_path = Path(SRC_IMAGE_PATH.format(message_id)).absolute()

    #Enregistrer temporairement les images sur Heroku
    save_image(message_id, src_image_path)
    
    #Vérifier les informations de l'utilisateur et réussir s'il n'est pas enregistré
    try:
        name_list, day_list = database.serch_data(user_id)
    except TypeError:
        pass
        
    #Numéro d'enregistrement
    num = len(name_list)
    
    #Si non enregistré, vérifiez votre nom
    if num == 0:
        line_bot_api.reply_message(
            event.reply_token,
            TextSendMessage(text="Quel est le nom de la personne sur la photo?"))
        #user_Ajouter un identifiant
        database.add_data(user_id)
    #Si vous avez une inscription, vérifiez qui est sur la photo
    elif num != 0:
        #Créez un dictionnaire avec le nom et le jour
        user_dict = dict(zip(name_list, day_list))
        
        #Lors de l'inscription d'une personne
        if num == 1:
            name_1 = name_list[0]
            buttons_template = TemplateSendMessage(
                alt_text="Qui est sur la photo?",
                template=ButtonsTemplate(
                    text="Qui est sur la photo?", actions=[
                        MessageAction(label=name_1, text=name_1),
                        MessageAction(label="Autre", text="Autre")
                    ]
                )
            )
        
        #En cas d'inscription de 2 personnes
        elif num == 2:
            name_1 = name_list[0]
            name_2 = name_list[1]
            buttons_template = TemplateSendMessage(
                alt_text="Qui est sur la photo?",
                template=ButtonsTemplate(
                    text="Qui est sur la photo?", actions=[
                        MessageAction(label=name_1, text=name_1),
                        MessageAction(label=name_2, text=name_2),
                        MessageAction(label="Autre", text="Autre")
                    ]
                )
            )
        
        #En cas d'inscription de 3 personnes
        elif num == 3:
            name_1 = name_list[0]
            name_2 = name_list[1]
            name_3 = name_list[2]
            buttons_template = TemplateSendMessage(
                alt_text="Qui est sur la photo?",
                template=ButtonsTemplate(
                    text="Qui est sur la photo?", actions=[
                        MessageAction(label=name_1, text=name_1),
                        MessageAction(label=name_2, text=name_2),
                        MessageAction(label=name_3, text=name_3),
                        MessageAction(label="Autre", text="Autre")
                    ]
                )
            )
        
        line_bot_api.reply_message(event.reply_token, buttons_template)

#Recevoir du texte
@handler.add(MessageEvent, message=TextMessage)
def handle_text(event):
    global text_name, birthday
    
    #Confirmation de la date de naissance si non enregistrée
    if num == 0:
        text_name = event.message.text
        select_day(event)
    #Si autre est sélectionné, vérifiez le nom
    elif event.message.text == "Autre":
        line_bot_api.reply_message(
            event.reply_token,
            TextSendMessage(text="Quel est le nom de la personne sur la photo?"))
    else:
        text_name = event.message.text
            
        #Si le nom est déjà enregistré, obtenez la date de naissance
        if text_name in name_list:
            birthday = user_dict[text_name]
        
        #Si si, sélectionnez la date de prise de vue, sinon sélectionnez la date de naissance
        select_day(event)
    
#Traiter et envoyer des images
@handler.add(PostbackEvent)
def handle_postback(event):
    global birthday
    
    #Message de nom de fichier_Chemin converti en identifiant
    main_image_path = MAIN_IMAGE_PATH.format(message_id)
    preview_image_path = PREVIEW_IMAGE_PATH.format(message_id)
    
    #Si l'anniversaire n'est pas défini
    if not "birthday" in globals():
        #Remplacer le résultat de l'action de sélection de date par anniversaire
        birthday = event.postback.params["date"]
        #Mettre à jour le nom et le jour
        database.update_data(user_id, num, text_name, birthday)
        
        #Sélection de la date de prise de vue
        select_day(event)
    #Si l'anniversaire est défini
    elif "birthday" in globals():
        #Traitement d'image
        date_the_image(src_image_path, Path(main_image_path).absolute(), event)
        date_the_image(src_image_path, Path(preview_image_path).absolute(), event)

        #Spécifiez l'image
        image_message = ImageSendMessage(
                original_content_url=f"https://<nom de l'application heroku>.herokuapp.com/{main_image_path}",
                preview_image_url=f"https://<nom de l'application heroku>.herokuapp.com/{preview_image_path}"
        )
    
        #Obtenir le journal
        app.logger.info(f"https://<nom de l'application heroku>.herokuapp.com/{main_image_path}")
        
        #Envoyer l'image
        line_bot_api.reply_message(event.reply_token, image_message)
        
        #Supprimer la variable anniversaire
        del birthday
        
        #Enregistrer les modifications de la base de données et se déconnecter
        database.close_db()

#Fonction de stockage d'image
def save_image(message_id: str, save_path: str) -> None:
    # message_Obtenir les données binaires de l'image à partir de l'identifiant
    message_content = line_bot_api.get_message_content(message_id)
    with open(save_path, "wb") as f:
        #Ecrire les données binaires acquises
        for chunk in message_content.iter_content():
            f.write(chunk)

#Fonction de sélection de la date
def select_day(event):
    #Si l'anniversaire est défini
    if "birthday" in globals():
        message = "Veuillez sélectionner la date de prise de vue"
    #Si l'anniversaire n'est pas défini
    elif not "birthday" in globals():
        message = "Veuillez sélectionner votre date de naissance"
    
    #Action de sélection de la date
    date_picker = TemplateSendMessage(
        alt_text=message,
        template=ButtonsTemplate(
            text=message,
            actions=[
                DatetimePickerTemplateAction(
                    label="Choix",
                    data="action=buy&itemid=1",
                    mode="date",
                    initial=str(datetime.date.today()),
                    max=str(datetime.date.today())
                )
            ]
        )
    )
    
    line_bot_api.reply_message(event.reply_token, date_picker)

#Fonction de traitement d'image
def date_the_image(src: str, desc: str, event) -> None:
    im = Image.open(src)
    draw = ImageDraw.Draw(im)
    font = ImageFont.truetype("./fonts/AquaKana.ttc", 50)
    
    #Obtenez la date de tournage
    date = event.postback.params["date"]
    #Calculez le nombre de jours après la naissance en soustrayant la date de naissance de la date de prise de vue
    how_old = datetime.datetime.strptime(date, "%Y-%m-%d") - datetime.datetime.strptime(str(birthday), "%Y-%m-%d")
    #Jours de naissance et 365(journée)Calculer le quotient et le reste avec
    years, days = divmod(how_old.days, 365)
    #Reste 30(journée)Calculer le quotient avec
    month = days // 30
    text = text_name + f"({years}Talent{month}mois)"
    
    #Taille du texte
    text_width = draw.textsize(text, font=font)[0]
    text_height = draw.textsize(text, font=font)[1]
    margin = 10
    x = im.width - text_width
    y = im.height - text_height
    #La taille du rectangle à dessiner
    rect_size = ((text_width + margin * 6), (text_height + margin * 2))
    #Dessinez un rectangle
    rect = Image.new("RGB", rect_size, (0, 0, 0))
    #Masque pour rendre le rectangle transparent
    mask = Image.new("L", rect_size, 128)
    
    #Coller un rectangle et un masque sur l'image
    im.paste(rect, (x - margin * 6, y - margin * 3), mask)
    #Rédaction de texte
    draw.text((x - margin * 3, y - margin * 2), text, fill=(255, 255, 255), font=font)
    im.save(desc)

if __name__ == "__main__":
    #app.run()
    port = int(os.getenv("PORT", 5000))
    app.run(host="0.0.0.0", port=port)
database.py

database.py


from flask_sqlalchemy import SQLAlchemy
from sqlalchemy import create_engine, Column, String, Date
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import Session
from sqlalchemy.orm.exc import NoResultFound
import datetime
import os

#Obtenir l'URL de la base de données à partir de la variable d'environnement
DATABASE_URL = os.environ["DATABASE_URL"]

engine = create_engine(DATABASE_URL)
Base = declarative_base()

#définition de table
class User(Base):
	__tablename__ = "user_list"
	user_id = Column("user_id", String(50), primary_key=True)
	name1 = Column("name1", String(10))
	day1 = Column("day1", Date)
	name2 = Column("name2", String(10))
	day2 = Column("day2", Date)
	name3 = Column("name3", String(10))
	day3 = Column("day3", Date)

Base.metadata.create_all(engine)
session = Session(bind=engine)

#user_recherche d'identifiant
def serch_data(user_id):
	try:
		#user_Recherche par identifiant
		res = session.query(User.name1, User.day1, User.name2, User.day2, User.name3, User.day3).filter(User.user_id==f"{user_id}").one()
		
		#Insérez respectivement le nom et le jour dans la liste
		name_list = [n for n in res if type(n) is str]
		day_list = [str(d) for d in res if type(d) is datetime.date]
		return name_list, day_list
	#user_Passer si l'identifiant n'est pas enregistré
	except NoResultFound:
		pass

#user_enregistrement d'identifiant
def add_data(user_id):
	session.add(User(user_id=f"{user_id}"))

#Mettre à jour les informations utilisateur
def update_data(user_id, num, text_name, birthday):
	user_data = session.query(User).filter(User.user_id==f"{user_id}").one()
	
	#Enregistrer le nom et le jour en fonction du nombre d'inscriptions
	if num == 0:
		user_data.name1 = text_name
		user_data.day1 = birthday
	elif num == 1:
		user_data.name2 = text_name
		user_data.day2 = birthday
	elif num == 2:
		user_data.name3 = text_name
		user_data.day3 = birthday
    
#Enregistrer les modifications de la base de données et se déconnecter
def close_db():
	session.commit()
	session.close()

Commentaire

main.py

Préparation

main.py


from flask import Flask, request, abort
from linebot import LineBotApi, WebhookHandler
from linebot.exceptions import InvalidSignatureError
from linebot.models import (FollowEvent, PostbackEvent, TemplateSendMessage, MessageAction,\
                                            ButtonsTemplate, DatetimePickerTemplateAction, ImageMessage, \
                                            ImageSendMessage, MessageEvent, TextMessage, TextSendMessage)
from pathlib import Path
from PIL import Image, ImageDraw, ImageFont, ImageFilter
import datetime
import os

import database

app = Flask(__name__)
app.debug = False

#Obtenir des variables d'environnement
YOUR_CHANNEL_ACCESS_TOKEN = os.environ["YOUR_CHANNEL_ACCESS_TOKEN"]
YOUR_CHANNEL_SECRET = os.environ["YOUR_CHANNEL_SECRET"]

line_bot_api = LineBotApi(YOUR_CHANNEL_ACCESS_TOKEN)
handler = WebhookHandler(YOUR_CHANNEL_SECRET)

#Chemin de référence de l'image
SRC_IMAGE_PATH = "static/images/{}.jpg "
MAIN_IMAGE_PATH = "static/images/{}_main.jpg "
PREVIEW_IMAGE_PATH = "static/images/{}_preview.jpg "

@app.route("/callback", methods=['POST'])
def callback():
    signature = request.headers['X-Line-Signature']

    body = request.get_data(as_text=True)
    app.logger.info("Request body: " + body)

    try:
        handler.handle(body, signature)
    except InvalidSignatureError:
        abort(400)

    return 'OK'

Importez le module et obtenez les variables d'environnement définies à l'avance, mais veuillez vérifier le rôle détaillé le cas échéant. Définissez le chemin de la source de référence d'image décrite plus loin, et remplacez la partie {} par message_id lorsque l'image a été reçue.

Envoyer du texte lorsque vous suivez

main.py


#Suivez l'événement
@handler.add(FollowEvent)
def handle_follow(event):
    line_bot_api.reply_message(
        event.reply_token,
        TextSendMessage(text=
        "Merci de vous être inscrit comme ami. J'écrirai dans l'image l'âge de la personne sur la photo. Veuillez m'envoyer une image."))

Un message est envoyé lorsqu'un utilisateur ajoute un ami.

Recevoir des images

main.py


#Recevoir des images
@handler.add(MessageEvent, message=ImageMessage)
def handle_image(event):
    global message_id, user_id, name_list, user_dict, num, src_image_path
    
    #Liste de stockage des informations utilisateur
    name_list = []
    day_list = []
    user_dict = {}
    #message_Obtenir l'identifiant
    message_id = event.message.id
    #user_Obtenir l'identifiant
    user_id = event.source.user_id
    
    #Message de nom de fichier_Chemin converti en identifiant
    src_image_path = Path(SRC_IMAGE_PATH.format(message_id)).absolute()

    #Enregistrer temporairement les images sur Heroku
    save_image(message_id, src_image_path)
    
    #Vérifier les informations de l'utilisateur et réussir s'il n'est pas enregistré
    try:
        name_list, day_list = database.serch_data(user_id)
    except TypeError:
        pass
        
    #Numéro d'enregistrement
    num = len(name_list)
    
    #Si non enregistré, vérifiez votre nom
    if num == 0:
        line_bot_api.reply_message(
            event.reply_token,
            TextSendMessage(text="Quel est le nom de la personne sur la photo?"))
        #user_Ajouter un identifiant
        database.add_data(user_id)
    #Si vous avez une inscription, vérifiez qui est sur la photo
    elif num != 0:
        #Créez un dictionnaire avec le nom et le jour
        user_dict = dict(zip(name_list, day_list))
        
        #Lors de l'inscription d'une personne
        if num == 1:
            name_1 = name_list[0]
            buttons_template = TemplateSendMessage(
                alt_text="Qui est sur la photo?",
                template=ButtonsTemplate(
                    text="Qui est sur la photo?", actions=[
                        MessageAction(label=name_1, text=name_1),
                        MessageAction(label="Autre", text="Autre")
                    ]
                )
            )
        
        #En cas d'inscription de 2 personnes
        elif num == 2:
            name_1 = name_list[0]
            name_2 = name_list[1]
            buttons_template = TemplateSendMessage(
                alt_text="Qui est sur la photo?",
                template=ButtonsTemplate(
                    text="Qui est sur la photo?", actions=[
                        MessageAction(label=name_1, text=name_1),
                        MessageAction(label=name_2, text=name_2),
                        MessageAction(label="Autre", text="Autre")
                    ]
                )
            )
        
        #En cas d'inscription de 3 personnes
        elif num == 3:
            name_1 = name_list[0]
            name_2 = name_list[1]
            name_3 = name_list[2]
            buttons_template = TemplateSendMessage(
                alt_text="Qui est sur la photo?",
                template=ButtonsTemplate(
                    text="Qui est sur la photo?", actions=[
                        MessageAction(label=name_1, text=name_1),
                        MessageAction(label=name_2, text=name_2),
                        MessageAction(label=name_3, text=name_3),
                        MessageAction(label="Autre", text="Autre")
                    ]
                )
            )
        
        line_bot_api.reply_message(event.reply_token, buttons_template)

Spécifiez la variable que vous souhaitez utiliser autre que MessageEvent dans global pour pouvoir y faire référence.

Dans l'instruction try name_list, day_list = database.serch_data (user_id), la name_list qui stocke le nom et la day_list qui stocke la date de naissance sont obtenues par la fonction serch_data du fichier database. Mise à jour de. À ce stade, si user_id n'est pas enregistré, aucune valeur n'est renvoyée et elle ne peut pas être mise à jour, spécifiez donc TypeError dans except.

En raison de la référence aux données, le traitement est divisé en cas où il n'y a pas d'enregistrement et en cas où il n'y a pas d'enregistrement. Sinon, vérifiez le nom et ajoutez user_id. Si tel est le cas, utilisez ʻuser_dict = dict (zip (name_list, day_list)) pour créer un dictionnaire faisant référence à la date de naissance en utilisant le nom comme clé. Après cela, le processus est divisé par 1 à 3 personnes enregistrées, et la personne sur la photo est confirmée avec TemplateSendMessage`.

Recevoir du texte

main.py


#Recevoir du texte
@handler.add(MessageEvent, message=TextMessage)
def handle_text(event):
    global text_name, birthday
    
    #Confirmation de la date de naissance si non enregistrée
    if num == 0:
        text_name = event.message.text
        select_day(event)
    #Si autre est sélectionné, vérifiez le nom
    elif event.message.text == "Autre":
        line_bot_api.reply_message(
            event.reply_token,
            TextSendMessage(text="Quel est le nom de la personne sur la photo?"))
    else:
        text_name = event.message.text
            
        #Si le nom est déjà enregistré, obtenez la date de naissance
        if text_name in name_list:
            birthday = user_dict[text_name]
        
        #Si si, sélectionnez la date de prise de vue, sinon sélectionnez la date de naissance
        select_day(event)

ʻIf num == 0: `est exécuté lorsque vous recevez une réponse confirmant le nom lorsqu'il n'y a pas d'enregistrement, et procède à la confirmation de la date de naissance après avoir obtenu le nom.

ʻElif event.message.text == "Other": est exécuté quand autre est sélectionné dans TemplateSendMessage` quand il y a enregistrement, et confirme le nom.

ʻElse: est exécuté lorsqu'un nom enregistré est sélectionné dans TemplateSendMessageautre que celui ci-dessus, et quand une réponse est reçue après confirmation du nom. S'il est enregistré, utilisezbirthday = user_dict [text_name]` pour obtenir la date de naissance en utilisant le nom comme clé. Pour les autres réponses, procédez à la sélection de la date et demandez-leur de sélectionner leur date de naissance.

Gestion des actions de sélection de date

main.py


#Traiter et envoyer des images
@handler.add(PostbackEvent)
def handle_postback(event):
    global birthday
    
    #Message de nom de fichier_Chemin converti en identifiant
    main_image_path = MAIN_IMAGE_PATH.format(message_id)
    preview_image_path = PREVIEW_IMAGE_PATH.format(message_id)
    
    #Si l'anniversaire n'est pas défini
    if not "birthday" in globals():
        #Remplacer le résultat de l'action de sélection de date par anniversaire
        birthday = event.postback.params["date"]
        #Mettre à jour le nom et le jour
        database.update_data(user_id, num, text_name, birthday)
        
        #Sélection de la date de prise de vue
        select_day(event)
    #Si l'anniversaire est défini
    elif "birthday" in globals():
        #Traitement d'image
        date_the_image(src_image_path, Path(main_image_path).absolute(), event)
        date_the_image(src_image_path, Path(preview_image_path).absolute(), event)

        #Spécifiez l'image
        image_message = ImageSendMessage(
                original_content_url=f"https://<nom de l'application heroku>.herokuapp.com/{main_image_path}",
                preview_image_url=f"https://<nom de l'application heroku>.herokuapp.com/{preview_image_path}"
        )
    
        #Obtenir le journal
        app.logger.info(f"https://<nom de l'application heroku>.herokuapp.com/{main_image_path}")
        
        #Envoyer l'image
        line_bot_api.reply_message(event.reply_token, image_message)
        
        #Supprimer la variable anniversaire
        del birthday
        
        #Enregistrer les modifications de la base de données et se déconnecter
        database.close_db()

ʻSi ce n'est pas "anniversaire" dans globals (): Si l'anniversaire n'est pas défini (la date de naissance a été sélectionnée par l'action de sélection de la date), le traitement est effectué. Obtenez la date sélectionnée par anniversaire = event.postback.params [" date "]et mettez à jour le nom de la base de données et la date de naissance avecdatabase.update_data (user_id, num, text_name, birthday)` , Demandez-leur de sélectionner la date de prise de vue.

ʻElif "anniversaire" dans globals (): effectue le traitement lorsque l'anniversaire est défini (la date de prise de vue a été sélectionnée par l'action de sélection de la date). image_message = ImageSendMessage( original_content_url = f "https: // .herokuapp.com / {main_image_path}", preview_image_url = f "https: // <nom de l'application heroku> .herokuapp.com / {preview_image_path}" Spécifiez l'image traitée avec) ʻet envoyez-la.

Supprimez la variable anniversaire avec del birthday afin qu'elle puisse être traitée normalement même lorsque les images sont envoyées en continu. Enfin, enregistrez les modifications de la base de données avec close_db () dans le fichier de base de données, déconnectez-vous et quittez.

Fonction de stockage d'image

main.py


#Fonction de stockage d'image
def save_image(message_id: str, save_path: str) -> None:
    # message_Obtenir les données binaires de l'image à partir de l'identifiant
    message_content = line_bot_api.get_message_content(message_id)
    with open(save_path, "wb") as f:
        #Ecrire les données binaires acquises
        for chunk in message_content.iter_content():
            f.write(chunk)

En fait, je voulais obtenir les informations Exif de l'image et entrer automatiquement la date de prise de vue, mais je ne pouvais pas l'obtenir avec cette méthode, j'ai donc pris la forme de l'action de sélection de la date ci-dessus comme une mesure minutieuse. Si quelqu'un sait comment le faire, faites-le moi savoir.

Fonction de sélection de la date

main.py


#Fonction de sélection de la date
def select_day(event):
    #Si l'anniversaire est défini
    if "birthday" in globals():
        message = "Veuillez sélectionner la date de prise de vue"
    #Si l'anniversaire n'est pas défini
    elif not "birthday" in globals():
        message = "Veuillez sélectionner votre date de naissance"
    
    #Action de sélection de la date
    date_picker = TemplateSendMessage(
        alt_text=message,
        template=ButtonsTemplate(
            text=message,
            actions=[
                DatetimePickerTemplateAction(
                    label="Choix",
                    data="action=buy&itemid=1",
                    mode="date",
                    initial=str(datetime.date.today()),
                    max=str(datetime.date.today())
                )
            ]
        )
    )
    
    line_bot_api.reply_message(event.reply_token, date_picker)

Cette fonction est exécutée une fois si la date de naissance est enregistrée, et deux fois au total si elle n'est pas enregistrée. Par conséquent, si l'anniversaire est défini, le message sera remplacé par la date de prise de vue, et s'il n'est pas défini, le message sera remplacé par la date de naissance.

Fonction de traitement d'image

main.py


#Fonction de traitement d'image
def date_the_image(src: str, desc: str, event) -> None:
    im = Image.open(src)
    draw = ImageDraw.Draw(im)
    font = ImageFont.truetype("./fonts/AquaKana.ttc", 50)
    
    #Obtenez la date de tournage
    date = event.postback.params["date"]
    #Calculez le nombre de jours après la naissance en soustrayant la date de naissance de la date de prise de vue
    how_old = datetime.datetime.strptime(date, "%Y-%m-%d") - datetime.datetime.strptime(str(birthday), "%Y-%m-%d")
    #Jours de naissance et 365(journée)Calculer le quotient et le reste avec
    years, days = divmod(how_old.days, 365)
    #Reste 30(journée)Calculer le quotient avec
    month = days // 30
    text = text_name + f"({years}Talent{month}mois)"
    
    #Taille du texte
    text_width = draw.textsize(text, font=font)[0]
    text_height = draw.textsize(text, font=font)[1]
    margin = 10
    x = im.width - text_width
    y = im.height - text_height
    #La taille du rectangle à dessiner
    rect_size = ((text_width + margin * 6), (text_height + margin * 2))
    #Dessinez un rectangle
    rect = Image.new("RGB", rect_size, (0, 0, 0))
    #Masque pour rendre le rectangle transparent
    mask = Image.new("L", rect_size, 128)
    
    #Coller un rectangle et un masque sur l'image
    im.paste(rect, (x - margin * 6, y - margin * 3), mask)
    #Rédaction de texte
    draw.text((x - margin * 3, y - margin * 2), text, fill=(255, 255, 255), font=font)
    im.save(desc)

Spécifiez la police et la taille à dessiner avec font = ImageFont.truetype (" ./fonts/AquaKana.ttc ", 50). Lorsque vous dessinez en japonais, certaines polices ne sont pas prises en charge, alors sélectionnez-en une qui contient du japonais dans l'aperçu.

Obtenez la date de prise de vue avec date = event.postback.params [" date "] et how_old = datetime.datetime.strptime (date,"% Y-% m-% d ") --datetime.datetime.strptime ( Utilisez str (anniversaire), "% Y-% m-% d") pour obtenir le nombre de jours obtenu en soustrayant la date de naissance de la date de prise de vue. Calculez le quotient et le reste en divisant le nombre de jours par 365 (jours) avec years, days = divmod (how_old.days, 365), et remplacez l'âge pour les années et le nombre de jours pour le reste pour les jours. month = days // 12 convertit les jours restants en mois, et text = text_name + f" ({years} years {month} mois) " définit ce qu'il faut écrire.

Utilisez rect_size = ((text_width + margin * 6), (text_height + margin * 2)) pour définir la taille du rectangle et du masque, et laissez une marge de 30px à gauche et à droite et 10px en haut et en bas du texte. Enfin, spécifiez la position du rectangle et du masque avec ʻim.paste (rect, (x --margin * 6, y --margin * 3), mask) ʻet collez-le, etdraw.text ( Écrivez le texte avec (x --margin * 3, y --margin * 2), text, fill = (255, 255, 255), font = font)et vous avez terminé.

Courir

main.py


if __name__ == "__main__":
    #app.run()
    port = int(os.getenv("PORT", 5000))
    app.run(host="0.0.0.0", port=port)

database.py

Préparation

database.py


from flask_sqlalchemy import SQLAlchemy
from sqlalchemy import create_engine, Column, String, Date
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import Session
from sqlalchemy.orm.exc import NoResultFound
import datetime
import os

#Obtenir l'URL de la base de données à partir de la variable d'environnement
DATABASE_URL = os.environ["DATABASE_URL"]

engine = create_engine(DATABASE_URL)
Base = declarative_base()

Tout d'abord, importez les modules requis dans le fichier de base de données. DATABASE_URL spécifie l'URL de la base de données définie dans heroku config. ʻEngine = create_engine (DATABASE_URL) , Base = declarative_base () `sera utilisé pour la prochaine création de table.

Créer une table

database.py


#définition de table
class User(Base):
	__tablename__ = "user_list"
	user_id = Column("user_id", String(50), primary_key=True)
	name1 = Column("name1", String(10))
	day1 = Column("day1", Date)
	name2 = Column("name2", String(10))
	day2 = Column("day2", Date)
	name3 = Column("name3", String(10))
	day3 = Column("day3", Date)

Base.metadata.create_all(engine)
session = Session(bind=engine)

Dans class User (Base):, spécifiez le nom de la table et le contenu de la colonne uniquement au moment de la création. À ce stade, il est nécessaire de spécifier primary_key dans une colonne, alors spécifiez-le dans une colonne qui ne provoque pas de duplication. Préparez-vous à vous connecter à la base de données avec Base.metadata.create_all (engine), session = Session (bind = engine).

Rechercher user_id

database.py


#user_recherche d'identifiant
def serch_data(user_id):
	try:
		#user_Recherche par identifiant
		res = session.query(User.name1, User.day1, User.name2, User.day2, User.name3, User.day3).filter(User.user_id==f"{user_id}").one()
		
		#Insérez respectivement le nom et le jour dans la liste
		name_list = [n for n in res if type(n) is str]
		day_list = [str(d) for d in res if type(d) is datetime.date]
		return name_list, day_list
	#user_Passer si l'identifiant n'est pas enregistré
	except NoResultFound:
		pass

res = session.query (User.name1, User.day1, User.name2, User.day2, User.name3, User.day3) .filter (User.user_id == f" {user_id} "). one () Obtenez des informations autres que user_id sur la ligne où user_id correspond à . Comme user_id ne peut pas être dupliqué, spécifiez un seul avec ʻone () `.

name_list = [n for n in res if type (n) is str] pour changer le type de chaîne (name) en name_list, day_list = [str (d) for d in res si type (d) est datetime.date] Insérez le type datetime.date (date de naissance) dans day_list sous forme de chaîne avec . Lorsque vous obtenez les informations avec res, la colonne vide obtient None, mais cela ne s'applique à aucun des types ci-dessus, donc vous n'avez pas besoin de ʻif ~ n'est pas None. Renvoie deux listes avec return name_list, day_list` et met à jour la liste dans main.py.

Si user_id n'est pas enregistré dans res, ce sera NoResultFound, alors spécifiez-le avec sauf pour l'éviter.

Modifier les informations utilisateur

database.py


#user_enregistrement d'identifiant
def add_data(user_id):
	session.add(User(user_id=f"{user_id}"))

#Mettre à jour les informations utilisateur
def update_data(user_id, num, text_name, birthday):
	user_data = session.query(User).filter(User.user_id==f"{user_id}").one()
	
	#Enregistrer le nom et le jour en fonction du nombre d'inscriptions
	if num == 0:
		user_data.name1 = text_name
		user_data.day1 = birthday
	elif num == 1:
		user_data.name2 = text_name
		user_data.day2 = birthday
	elif num == 2:
		user_data.name3 = text_name
		user_data.day3 = birthday

Si user_id n'est pas enregistré dans session.add (User (user_id = f" {user_id} ")), il sera ajouté.

ʻUser_data = session.query (User) .filter (User.user_id == f "{user_id}"). Obtenez des informations sur l'utilisateur avec one () `, branchez avec if et mettez à jour la colonne appropriée. À ce stade, j'ai essayé d'obtenir et de mettre à jour uniquement les colonnes nécessaires, mais une erreur s'est produite, il semble donc plus sûr d'obtenir toutes les informations.

Déconnectez la base de données

database.py


#Enregistrer les modifications de la base de données et se déconnecter
def close_db():
	session.commit()
	session.close()

Enregistrez les modifications de la base de données avec session.commit () et déconnectez-vous avec session.close () pour terminer.

organisation des fichiers

Résumé

Environ deux mois après le dernier achèvement, j'ai réussi à incorporer la base de données avec laquelle je travaillais pour la première fois au point que je visais. En cours de route, j'ai essayé de nouvelles choses telles que le passage de MySQL à PostgreSQL et l'utilisation de sqlalchemy que je n'avais pas prévu d'utiliser au début, mais j'ai pu l'implémenter de manière relativement fluide par rapport à la fois précédente, et j'ai pu ressentir une légère croissance. ..

Pour ce bot, j'aimerais le compléter une fois et créer une application qui améliore les inconvénients quand j'ai le temps.

référence

Recommended Posts

[Python] Créez un linebot pour écrire le nom et l'âge sur l'image
[Python] Combiner des listes contenant des nombres en chaînes et les écrire dans un fichier de sortie
Comment créer un téléchargeur d'image avec Bottle (Python)
Créez un environnement shell et python décent sur Windows
Création d'une bibliothèque Python pour écrire des inclusions complexes et les réduire de manière facile à lire
Comment mettre Pyenv sur Amazon Linux et Ubuntu pour créer un environnement Python 3.6.0
Essayez d'exécuter Schedule pour démarrer et arrêter des instances dans AWS Lambda (Python)
[python] Remplacez le nom du fichier image par un numéro de série
[Python] Créez un linebot qui dessine n'importe quelle date sur une photo
Comment écrire une classe méta qui prend en charge à la fois python2 et python3
Comment créer un fichier ISO (image CD) sous Linux
Créer un environnement python dans centos
Convertir une chaîne en image
5 façons de créer un chatbot Python
[MariaDB] Installez MariaDB sous Linux et créez une base de données et un utilisateur opérationnel.
Essayez d'extraire une chaîne de caractères d'une image avec Python3
Créez un lot planifié simple à l'aide de l'image Python de Docker et de parse-crontab
Python vba pour créer une chaîne de date pour créer un nom de fichier
La route pour installer Python et Flask sur un PC hors ligne
J'ai essayé de créer un linebot (implémentation)
Qiita (1) Comment écrire un nom de code
J'ai essayé de créer un linebot (préparation)
Créez une image factice avec Python + PIL.
Créez un environnement python sur votre Mac
Un mémo contenant Python2.7 et Python3 dans CentOS
Créer un environnement OpenCV3 + python3 sur OSX
[Python] Créez un LineBot qui s'exécute régulièrement
Compressez les données python et écrivez sur sqlite
Créez des raccourcis pour exécuter des fichiers Python sur le terminal avec VScode
Procédure de création d'un environnement virtuel Python avec VS Code sous Windows
Un programme python qui redimensionne une vidéo et la transforme en image
Essayez d'écrire du code python pour générer du code go - Essayez de porter JSON-to-Go et ainsi de suite
Créez un environnement Python 3 avec pyenv sur Mac et affichez des graphiques Network X
J'ai créé un exemple pour accéder à Salesforce en utilisant Python et Bottle
J'ai créé un script POST pour créer un problème sur Github et l'enregistrer dans le projet
L'histoire de la création d'un outil pour charger une image avec Python ⇒ l'enregistrer sous un autre nom
Créer en Python sans fichier image factice dans Django et tester le téléchargement de l'image
[Python] Compréhension de liste Différentes façons de créer une liste
Modifier Excel à partir de Python pour créer un tableau croisé dynamique
Une histoire d'essayer d'installer uwsgi sur une instance EC2 et d'échouer
Je veux créer une fenêtre avec Python
Comment créer un fichier JSON en Python
Je veux écrire un élément dans un fichier avec numpy et le vérifier.
J'ai un package Python privé mais je souhaite installer pipenv sur GitHub Actions et créer une image Docker
Créer une carte Web en utilisant Python et GDAL
Pourquoi Python doit-il écrire un deux-points?
Créer une image avec des caractères avec python (japonais)
J'ai fait un modèle de classification d'images et essayé de le déplacer sur mobile
Étapes pour créer un bot Twitter avec Python
[Venv] Créer un environnement virtuel python sur Ubuntu
Essayez de créer une nouvelle commande sous Linux
Écrivons un programme Python et exécutons-le
Recherche d'un moyen efficace d'écrire un Dockerfile avec Python avec de la poésie
Pour écrire dans Error Repoting en Python sur GAE
[Python] J'ai créé un script qui coupe et colle automatiquement les fichiers du PC local sur un SSD externe.
Je veux écrire dans un fichier avec Python