[Python] Create a linebot to write a name and age on an image

What I made

Improved the previously created Line bot that draws an arbitrary date on a photo, saves the name and date of birth in the database, and how old it is from the shooting date I created a linebot that calculates the photo or writes it to the image. Click here for github

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

environment

What not to write in this article

--LineBot channel creation --Deploy to heroku

Full text

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

#Get environment variables
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)

#Image referrer path
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'

#Follow event
@handler.add(FollowEvent)
def handle_follow(event):
    line_bot_api.reply_message(
        event.reply_token,
        TextSendMessage(text=
        "Thank you for registering as a friend. I'll write in the image how old the person in the picture is. Please send me an image."))

#Receiving images
@handler.add(MessageEvent, message=ImageMessage)
def handle_image(event):
    global message_id, user_id, name_list, user_dict, num, src_image_path
    
    #List for storing user information
    name_list = []
    day_list = []
    user_dict = {}
    #message_Get id
    message_id = event.message.id
    #user_Get id
    user_id = event.source.user_id
    
    #File name message_Path converted to id
    src_image_path = Path(SRC_IMAGE_PATH.format(message_id)).absolute()

    #Temporarily save images to Heroku
    save_image(message_id, src_image_path)
    
    #Check user information and pass if not registered
    try:
        name_list, day_list = database.serch_data(user_id)
    except TypeError:
        pass
        
    #Registration number
    num = len(name_list)
    
    #If not registered, check your name
    if num == 0:
        line_bot_api.reply_message(
            event.reply_token,
            TextSendMessage(text="What is the name of the person in the photo?"))
        #user_Add id
        database.add_data(user_id)
    #If you have a registration, check who is in the picture
    elif num != 0:
        #Create a dictionary with name and day
        user_dict = dict(zip(name_list, day_list))
        
        #When registering one person
        if num == 1:
            name_1 = name_list[0]
            buttons_template = TemplateSendMessage(
                alt_text="Who is in the picture?",
                template=ButtonsTemplate(
                    text="Who is in the picture?", actions=[
                        MessageAction(label=name_1, text=name_1),
                        MessageAction(label="Other", text="Other")
                    ]
                )
            )
        
        #In case of 2 person registration
        elif num == 2:
            name_1 = name_list[0]
            name_2 = name_list[1]
            buttons_template = TemplateSendMessage(
                alt_text="Who is in the picture?",
                template=ButtonsTemplate(
                    text="Who is in the picture?", actions=[
                        MessageAction(label=name_1, text=name_1),
                        MessageAction(label=name_2, text=name_2),
                        MessageAction(label="Other", text="Other")
                    ]
                )
            )
        
        #In case of 3 person registration
        elif num == 3:
            name_1 = name_list[0]
            name_2 = name_list[1]
            name_3 = name_list[2]
            buttons_template = TemplateSendMessage(
                alt_text="Who is in the picture?",
                template=ButtonsTemplate(
                    text="Who is in the picture?", actions=[
                        MessageAction(label=name_1, text=name_1),
                        MessageAction(label=name_2, text=name_2),
                        MessageAction(label=name_3, text=name_3),
                        MessageAction(label="Other", text="Other")
                    ]
                )
            )
        
        line_bot_api.reply_message(event.reply_token, buttons_template)

#Receiving text
@handler.add(MessageEvent, message=TextMessage)
def handle_text(event):
    global text_name, birthday
    
    #Confirmation of date of birth if not registered
    if num == 0:
        text_name = event.message.text
        select_day(event)
    #If other is selected, check the name
    elif event.message.text == "Other":
        line_bot_api.reply_message(
            event.reply_token,
            TextSendMessage(text="What is the name of the person in the photo?"))
    else:
        text_name = event.message.text
            
        #If the name is already registered, get the date of birth
        if text_name in name_list:
            birthday = user_dict[text_name]
        
        #If if, select the shooting date, otherwise select the date of birth
        select_day(event)
    
#Process and send images
@handler.add(PostbackEvent)
def handle_postback(event):
    global birthday
    
    #File name message_Path converted to id
    main_image_path = MAIN_IMAGE_PATH.format(message_id)
    preview_image_path = PREVIEW_IMAGE_PATH.format(message_id)
    
    #If birthday is undefined
    if not "birthday" in globals():
        #Substitute the result of the date selection action for birthday
        birthday = event.postback.params["date"]
        #Update name and day
        database.update_data(user_id, num, text_name, birthday)
        
        #Selection of shooting date
        select_day(event)
    #If birthday is defined
    elif "birthday" in globals():
        #Image processing
        date_the_image(src_image_path, Path(main_image_path).absolute(), event)
        date_the_image(src_image_path, Path(preview_image_path).absolute(), event)

        #Specify image
        image_message = ImageSendMessage(
                original_content_url=f"https://<heroku app name>.herokuapp.com/{main_image_path}",
                preview_image_url=f"https://<heroku app name>.herokuapp.com/{preview_image_path}"
        )
    
        #Get log
        app.logger.info(f"https://<heroku app name>.herokuapp.com/{main_image_path}")
        
        #Send image
        line_bot_api.reply_message(event.reply_token, image_message)
        
        #Delete the variable birthday
        del birthday
        
        #Save database changes and disconnect
        database.close_db()

#Image storage function
def save_image(message_id: str, save_path: str) -> None:
    # message_Get binary data of image from id
    message_content = line_bot_api.get_message_content(message_id)
    with open(save_path, "wb") as f:
        #Write the acquired binary data
        for chunk in message_content.iter_content():
            f.write(chunk)

#Date selection function
def select_day(event):
    #If birthday is defined
    if "birthday" in globals():
        message = "Please select the shooting date"
    #If birthday is undefined
    elif not "birthday" in globals():
        message = "Please select your date of birth"
    
    #Date selection action
    date_picker = TemplateSendMessage(
        alt_text=message,
        template=ButtonsTemplate(
            text=message,
            actions=[
                DatetimePickerTemplateAction(
                    label="Choice",
                    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)

#Image processing function
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)
    
    #Get the shooting date
    date = event.postback.params["date"]
    #Calculate the number of days after birth by subtracting the date of birth from the shooting date
    how_old = datetime.datetime.strptime(date, "%Y-%m-%d") - datetime.datetime.strptime(str(birthday), "%Y-%m-%d")
    #Days of birth and 365(Day)Calculate quotient and remainder with
    years, days = divmod(how_old.days, 365)
    #Remainder 30(Day)Calculate quotient with
    month = days // 30
    text = text_name + f"({years}Talent{month}months)"
    
    #Text size
    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
    #The size of the rectangle to draw
    rect_size = ((text_width + margin * 6), (text_height + margin * 2))
    #Rectangle drawing
    rect = Image.new("RGB", rect_size, (0, 0, 0))
    #Mask to make the rectangle transparent
    mask = Image.new("L", rect_size, 128)
    
    #Paste the rectangle and mask on the image
    im.paste(rect, (x - margin * 6, y - margin * 3), mask)
    #Writing text
    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

#Get the database URL from an environment variable
DATABASE_URL = os.environ["DATABASE_URL"]

engine = create_engine(DATABASE_URL)
Base = declarative_base()

#table definition
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_id search
def serch_data(user_id):
	try:
		#user_Search by id
		res = session.query(User.name1, User.day1, User.name2, User.day2, User.name3, User.day3).filter(User.user_id==f"{user_id}").one()
		
		#Insert name and day into the list respectively
		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_Pass if id is not registered
	except NoResultFound:
		pass

#user_id registration
def add_data(user_id):
	session.add(User(user_id=f"{user_id}"))

#Update user information
def update_data(user_id, num, text_name, birthday):
	user_data = session.query(User).filter(User.user_id==f"{user_id}").one()
	
	#Save name and day according to the number of registrations
	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
    
#Save database changes and disconnect
def close_db():
	session.commit()
	session.close()

Commentary

main.py

Preparation

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

#Get environment variables
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)

#Image referrer path
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'

Import the module and get the environment variables set in advance, but I hope you can check the detailed role as appropriate. Set the path of the image reference source described later, and replace the {} part with the message_id when the image was received.

Send text when following

main.py


#Follow event
@handler.add(FollowEvent)
def handle_follow(event):
    line_bot_api.reply_message(
        event.reply_token,
        TextSendMessage(text=
        "Thank you for registering as a friend. I'll write in the image how old the person in the picture is. Please send me an image."))

A message is sent when a user adds a friend.

Receiving images

main.py


#Receiving images
@handler.add(MessageEvent, message=ImageMessage)
def handle_image(event):
    global message_id, user_id, name_list, user_dict, num, src_image_path
    
    #List for storing user information
    name_list = []
    day_list = []
    user_dict = {}
    #message_Get id
    message_id = event.message.id
    #user_Get id
    user_id = event.source.user_id
    
    #File name message_Path converted to id
    src_image_path = Path(SRC_IMAGE_PATH.format(message_id)).absolute()

    #Temporarily save images to Heroku
    save_image(message_id, src_image_path)
    
    #Check user information and pass if not registered
    try:
        name_list, day_list = database.serch_data(user_id)
    except TypeError:
        pass
        
    #Registration number
    num = len(name_list)
    
    #If not registered, check your name
    if num == 0:
        line_bot_api.reply_message(
            event.reply_token,
            TextSendMessage(text="What is the name of the person in the photo?"))
        #user_Add id
        database.add_data(user_id)
    #If you have a registration, check who is in the picture
    elif num != 0:
        #Create a dictionary with name and day
        user_dict = dict(zip(name_list, day_list))
        
        #When registering one person
        if num == 1:
            name_1 = name_list[0]
            buttons_template = TemplateSendMessage(
                alt_text="Who is in the picture?",
                template=ButtonsTemplate(
                    text="Who is in the picture?", actions=[
                        MessageAction(label=name_1, text=name_1),
                        MessageAction(label="Other", text="Other")
                    ]
                )
            )
        
        #In case of 2 person registration
        elif num == 2:
            name_1 = name_list[0]
            name_2 = name_list[1]
            buttons_template = TemplateSendMessage(
                alt_text="Who is in the picture?",
                template=ButtonsTemplate(
                    text="Who is in the picture?", actions=[
                        MessageAction(label=name_1, text=name_1),
                        MessageAction(label=name_2, text=name_2),
                        MessageAction(label="Other", text="Other")
                    ]
                )
            )
        
        #In case of 3 person registration
        elif num == 3:
            name_1 = name_list[0]
            name_2 = name_list[1]
            name_3 = name_list[2]
            buttons_template = TemplateSendMessage(
                alt_text="Who is in the picture?",
                template=ButtonsTemplate(
                    text="Who is in the picture?", actions=[
                        MessageAction(label=name_1, text=name_1),
                        MessageAction(label=name_2, text=name_2),
                        MessageAction(label=name_3, text=name_3),
                        MessageAction(label="Other", text="Other")
                    ]
                )
            )
        
        line_bot_api.reply_message(event.reply_token, buttons_template)

Specify the variable you want to use other than MessageEvent in global so that it can be referenced.

In the try statement name_list, day_list = database.serch_data (user_id), the name_list that stores the name and the day_list that stores the date of birth are obtained by the serch_data function of the database file. Update to. At this time, if user_id is not registered, no value is returned and it cannot be updated, so specify TypeError in except.

As a result of referring to the data, the processing is divided depending on whether there is no registration or not. If not, check the name and add user_id. If there is, use ʻuser_dict = dict (zip (name_list, day_list))to create a dictionary to refer to the date of birth using the name as a key. After that, the process is further divided by 1 to 3 registered people, and the person in the picture is confirmed byTemplateSendMessage`.

Receiving text

main.py


#Receiving text
@handler.add(MessageEvent, message=TextMessage)
def handle_text(event):
    global text_name, birthday
    
    #Confirmation of date of birth if not registered
    if num == 0:
        text_name = event.message.text
        select_day(event)
    #If other is selected, check the name
    elif event.message.text == "Other":
        line_bot_api.reply_message(
            event.reply_token,
            TextSendMessage(text="What is the name of the person in the photo?"))
    else:
        text_name = event.message.text
            
        #If the name is already registered, get the date of birth
        if text_name in name_list:
            birthday = user_dict[text_name]
        
        #If if, select the shooting date, otherwise select the date of birth
        select_day(event)

ʻIf num == 0:` is executed when you receive a reply that confirms the name when there is no registration, and proceeds to confirm the date of birth after obtaining the name.

ʻElif event.message.text ==" Other ":is executed when Other is selected inTemplateSendMessage` when there is registration, and confirms the name.

ʻElse:is executed when a registered name is selected inTemplateSendMessageother than the above, and when a reply is received after confirming the name. If you have already registered, usebirthday = user_dict [text_name]` to get the date of birth using the name as a key. For other replies, proceed to date selection and ask them to select their date of birth.

Handling date selection actions

main.py


#Process and send images
@handler.add(PostbackEvent)
def handle_postback(event):
    global birthday
    
    #File name message_Path converted to id
    main_image_path = MAIN_IMAGE_PATH.format(message_id)
    preview_image_path = PREVIEW_IMAGE_PATH.format(message_id)
    
    #If birthday is undefined
    if not "birthday" in globals():
        #Substitute the result of the date selection action for birthday
        birthday = event.postback.params["date"]
        #Update name and day
        database.update_data(user_id, num, text_name, birthday)
        
        #Selection of shooting date
        select_day(event)
    #If birthday is defined
    elif "birthday" in globals():
        #Image processing
        date_the_image(src_image_path, Path(main_image_path).absolute(), event)
        date_the_image(src_image_path, Path(preview_image_path).absolute(), event)

        #Specify image
        image_message = ImageSendMessage(
                original_content_url=f"https://<heroku app name>.herokuapp.com/{main_image_path}",
                preview_image_url=f"https://<heroku app name>.herokuapp.com/{preview_image_path}"
        )
    
        #Get log
        app.logger.info(f"https://<heroku app name>.herokuapp.com/{main_image_path}")
        
        #Send image
        line_bot_api.reply_message(event.reply_token, image_message)
        
        #Delete the variable birthday
        del birthday
        
        #Save database changes and disconnect
        database.close_db()

ʻIf not "birthday" in globals (): does what happens when birthday is undefined (the date of birth was selected by the date selection action). Get the date selected by birthday = event.postback.params [" date "]and update the database name and date of birth withdatabase.update_data (user_id, num, text_name, birthday)` , Ask them to select the shooting date.

ʻElif" birthday "in globals ():Performs processing when birthday is defined (the shooting date was selected by the date selection action).image_message = ImageSendMessage( original_content_url = f "https: // .herokuapp.com / {main_image_path}", preview_image_url = f "https: // .herokuapp.com / {preview_image_path}" Specify the processed image with) `and send it.

Delete the variable birthday with del birthday so that it can be processed normally even when images are sent continuously. Finally, save the database changes with close_db () in the database file, disconnect and exit.

Image storage function

main.py


#Image storage function
def save_image(message_id: str, save_path: str) -> None:
    # message_Get binary data of image from id
    message_content = line_bot_api.get_message_content(message_id)
    with open(save_path, "wb") as f:
        #Write the acquired binary data
        for chunk in message_content.iter_content():
            f.write(chunk)

Actually, I wanted to get the Exif information of the image and automatically enter the shooting date, but I could not get it with this method, so I took the form of the above date selection action as a painstaking measure. If anyone knows how to do it, please let me know.

Date selection function

main.py


#Date selection function
def select_day(event):
    #If birthday is defined
    if "birthday" in globals():
        message = "Please select the shooting date"
    #If birthday is undefined
    elif not "birthday" in globals():
        message = "Please select your date of birth"
    
    #Date selection action
    date_picker = TemplateSendMessage(
        alt_text=message,
        template=ButtonsTemplate(
            text=message,
            actions=[
                DatetimePickerTemplateAction(
                    label="Choice",
                    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)

This function is executed once if the date of birth is registered, and twice in total if it is not registered. Therefore, if birthday is defined, the message will be changed to the shooting date, and if it is not defined, the message will be changed to the date of birth.

Image processing function

main.py


#Image processing function
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)
    
    #Get the shooting date
    date = event.postback.params["date"]
    #Calculate the number of days after birth by subtracting the date of birth from the shooting date
    how_old = datetime.datetime.strptime(date, "%Y-%m-%d") - datetime.datetime.strptime(str(birthday), "%Y-%m-%d")
    #Days of birth and 365(Day)Calculate quotient and remainder with
    years, days = divmod(how_old.days, 365)
    #Remainder 30(Day)Calculate quotient with
    month = days // 30
    text = text_name + f"({years}Talent{month}months)"
    
    #Text size
    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
    #The size of the rectangle to draw
    rect_size = ((text_width + margin * 6), (text_height + margin * 2))
    #Rectangle drawing
    rect = Image.new("RGB", rect_size, (0, 0, 0))
    #Mask to make the rectangle transparent
    mask = Image.new("L", rect_size, 128)
    
    #Paste the rectangle and mask on the image
    im.paste(rect, (x - margin * 6, y - margin * 3), mask)
    #Writing text
    draw.text((x - margin * 3, y - margin * 2), text, fill=(255, 255, 255), font=font)
    im.save(desc)

Specify the font and size to draw with font = ImageFont.truetype ("./fonts/AquaKana.ttc", 50) . When drawing Japanese, some fonts are not supported, so select one that contains Japanese in the preview.

Get the shooting date with date = event.postback.params [" date "] and how_old = datetime.datetime.strptime (date,"% Y-% m-% d ") --datetime.datetime.strptime ( Use str (birthday), "% Y-% m-% d") to get the number of days obtained by subtracting the date of birth from the shooting date. Calculate the quotient and remainder by dividing the number of days by 365 (days) with years, days = divmod (how_old.days, 365), and substitute the age for years and the number of days for the remainder for days. month = days // 12 converts the remaining days to months, andtext = text_name + f "({years} years {month} months)"defines what to write.

Use rect_size = ((text_width + margin * 6), (text_height + margin * 2)) to set the size of the rectangle and mask, and leave a margin of 30px on the left and right and 10px on the top and bottom of the text. Finally, specify the position of the rectangle and mask with ʻim.paste (rect, (x --margin * 6, y --margin * 3), mask) and paste it, and draw.text ( Write the text with (x --margin * 3, y --margin * 2), text, fill = (255, 255, 255), font = font) `and you're done.

Run

main.py


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

database.py

Preparation

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

#Get the database URL from an environment variable
DATABASE_URL = os.environ["DATABASE_URL"]

engine = create_engine(DATABASE_URL)
Base = declarative_base()

First, import the required modules in the database file. DATABASE_URL specifies the database URL set in heroku config. ʻEngine = create_engine (DATABASE_URL) , Base = declarative_base () `is used for the next table creation.

Create table

database.py


#table definition
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)

In class User (Base):, specify the table name and column contents only at the time of creation. At this time, it is necessary to specify primary_key in some column, so specify it in a column that does not cause duplication. Prepare to connect to the database with Base.metadata.create_all (engine), session = Session (bind = engine).

Search for user_id

database.py


#user_id search
def serch_data(user_id):
	try:
		#user_Search by id
		res = session.query(User.name1, User.day1, User.name2, User.day2, User.name3, User.day3).filter(User.user_id==f"{user_id}").one()
		
		#Insert name and day into the list respectively
		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_Pass if id is not registered
	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 () Gets information other than the user_id of the line where the user_id matches with . Since user_id cannot be duplicated, specify only one with ʻone () `.

name_list = [n for n in res if type (n) is str] to change str type (name) to name_list, day_list = [str (d) for d in res if type (d) is datetime.date] Insert datetime.date type (date of birth) into day_list as a string with . When you get the information with res, the empty column gets None, but it doesn't apply to either of the above types, so you don't need ʻif ~ is not None. Returns two lists with return name_list, day_list` and updates the list in main.py.

If user_id is not registered in res, it will be NoResultFound, so specify it with except to avoid it.

Change user information

database.py


#user_id registration
def add_data(user_id):
	session.add(User(user_id=f"{user_id}"))

#Update user information
def update_data(user_id, num, text_name, birthday):
	user_data = session.query(User).filter(User.user_id==f"{user_id}").one()
	
	#Save name and day according to the number of registrations
	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

If user_id is not registered in session.add (User (user_id = f" {user_id} ")), it will be added.

ʻUser_data = session.query (User) .filter (User.user_id == f "{user_id}"). Get user information with one () `, branch with if and update the appropriate column. At this time, I tried to get and update only the necessary columns, but an error occurred, so it seems safer to get all the information.

Disconnect database

database.py


#Save database changes and disconnect
def close_db():
	session.commit()
	session.close()

Save the database changes with session.commit () and disconnect with session.close () to finish.

file organization

Summary

About two months after last completion, I managed to incorporate the database I was dealing with for the first time to the point I was aiming for. On the way, I tried new things such as changing from MySQL to PostgreSQL and using sqlalchemy that I did not plan to use at the beginning, but I was able to implement it relatively smoothly compared to the previous time, and I could feel a slight growth. ..

For this bot, I would like to complete it once and create an application that improves inconveniences when I have time.

reference

-How to use PostgreSQL pgAdmin 4 (Refer data from startup) -[[Python ORM] Summary of basic SQL queries with SQLAlchemy](https://qiita.com/tomo0/items/a762b1bc0f192a55eae8#select%E3%82%AB%E3%83%A9%E3%83%A0 % E3% 82% 92% E6% 8C% 87% E5% AE% 9A) -[Create a web app with Flask and PostgreSQL and run it for free on Heroku](https://qiita.com/croquette0212/items/9b4dc5377e7d6f292671#heroku%E3%81%ABdeploy%E3%81%99%E3%82 % 8B)

Recommended Posts

[Python] Create a linebot to write a name and age on an image
[Python] Concatenate a List containing numbers and write it to an output file.
How to create an image uploader in Bottle (Python)
Create a decent shell and python environment on Windows
Created a Python library to write complex comprehensions and reduce in an easy-to-read manner
How to create a Python 3.6.0 environment by putting pyenv on Amazon Linux and Ubuntu
Try running a Schedule to start and stop an instance on AWS Lambda (Python)
[python] Change the image file name to a serial number
[Python] Create a linebot that draws any date on a photo
How to write a metaclass that supports both python2 and python3
How to create an ISO file (CD image) on Linux
Create a python environment on centos
Convert a string to an image
5 Ways to Create a Python Chatbot
[MariaDB] Install MariaDB on Linux and create a DB and an operating user.
Try to extract a character string from an image with Python3
How to create a heatmap with an arbitrary domain in Python
Create a simple scheduled batch using Docker's Python Image and parse-crontab
Python vba to create a date string for creating a file name
The road to installing Python and Flask on an offline PC
I tried to create a linebot (implementation)
Qiita (1) How to write a code name
I tried to create a linebot (preparation)
Create a dummy image with Python + PIL.
Create a python environment on your Mac
A memo with Python2.7 and Python3 on CentOS
Create an OpenCV3 + python3 environment on OSX
[Python] Create a LineBot that runs regularly
Compress python data and write to sqlite
Create a shortcut to run a Python file in VScode on your terminal
Steps to create a Python virtual environment with VS Code on Windows
A python program that resizes a video and turns it into an image
Try to write python code to generate go code --Try porting JSON-to-Go and so on
Create a Python3 environment with pyenv on Mac and display a NetworkX graph
I tried to create a sample to access Salesforce using Python and Bottle
I made a POST script to create an issue on Github and register it in the Project
The story of making a tool to load an image with Python ⇒ save it as another name
I want to pass an argument to a python function and execute it from PHP on a web server
Create a Python image in Django without a dummy image file and test the image upload
[Python] List Comprehension Various ways to create a list
Edit Excel from Python to create a PivotTable
A story about trying to install uwsgi on an EC2 instance and failing
How to create a Python virtual environment (venv)
I want to create a window in Python
How to create a JSON file in Python
I want to write an element to a file with numpy and check it.
I have a private Python package but I want to install pipenv on GitHub Actions and build a Docker Image
Create a web map using Python and GDAL
A complete guidebook to using pyenv, pip and python in an offline environment
Why does Python have to write a colon?
Create an image with characters in python (Japanese)
I made an image classification model and tried to move it on mobile
Steps to create a Twitter bot with python
[Venv] Create a python virtual environment on Ubuntu
Try to create a new command on linux
Let's write a Python program and run it
Searching for an efficient way to write a Dockerfile in Python with poetry
To write to Error Repoting in Python on GAE
[Python] I made a script that automatically cuts and pastes files on a local PC to an external SSD.
Create a Mac app using py2app and Python3! !!
I want to write to a file with Python