・ Depuis que j'ai déménagé, j'ai commencé à utiliser le bus pour me déplacer. ・ Je prends une photo des horaires de bus avec un iPhone et je la vérifie une par une parmi les "photos". ・ L'itinéraire de navettage est [Accueil ↔︎ (bus) ↔︎ Gare la plus proche ↔︎ (train) ↔︎ Gare entreprise], surtout sur le chemin du retour, je souhaite monter dans le bus dès que j'arrive à la gare la plus proche. ・ Il est difficile d'effectuer l'opération suivante à chaque fois que vous vérifiez l'heure du bus. (Lancez l'application photo sur votre iphone) → (Recherchez l'heure de bus prise depuis l'application photo) ・ Je souhaite connaître l'heure du bus plus efficacement. ・ Je veux créer quelque chose d'utile pour ma vie en utilisant python, LINEBot, etc. que j'étudie. ・ Bien que je sois diligent dans mes études, je commence à en avoir assez de créer des applications qui imitent les gens. ・ Ce qui précède est l'arrière-plan de la création de cette application. (Attention) La source est un débutant, donc elle est sale (veuillez me pardonner). (Remarque) Je prends le calendrier du fichier csv, je le teste dans l'environnement local, puis je le déploie sur Heroku (il peut y avoir un moyen plus efficace).
↓↓↓↓↓↓↓ (Image terminée) ↓↓↓↓↓↓↓↓ Lorsque vous envoyez "Go" à LINE Bot, l'image du bus, l'heure de bus de l'arrêt de bus le plus proche de votre domicile à la gare et l'heure de bus suivante sont renvoyées. Lorsque vous dites «retour», le temps de bus de la gare la plus proche à l'arrêt de bus le plus proche de votre domicile vous sera retourné.
・ Comment créer un canal LINE Bot ・ Détails sur la façon de déployer sur Heroku
(1) Contexte de la création de l'application (2) Ce qu'il ne faut pas écrire dans cet article et la structure de l'article (3) Construction de l'environnement (4) Contrôle de fonctionnement dans l'environnement local (5) Incorporer dans LINE Bot en utilisant Heroku
· Mac ・ Python3 ・ Sqlite3 ・ PostgreSQL ・ Heroku ・ Flacon
Tout d'abord, créez un répertoire linebot_jikokuhyou sur le bureau, créez un environnement virtuel dans le répertoire et démarrez-le.
python3 -m venv .
source bin/activate
Commencez par tester dans votre environnement local. La base de données utilise sql. Importez le fichier csv préparé à l'avance dans sql et vérifiez si le programme fonctionne normalement localement avec Flask.
Préparez le répertoire et les fichiers comme indiqué ci-dessous. Pour iki.csv et kaeri.csv, utilisez les fichiers préparés par vous-même (décrits plus loin). Les fichiers autres que ceux ci-dessus seront créés en tant que fichiers vides.
linebot_jikokuhyou
├csv_kakou.py
├csv_to_sql.py
├local_main.py
├jikoku_main.py
├assets
│ ├database.py
│ ├models.py
│ ├__ini__.py
│ ├iki.csv (fichier csv préparé par vous-même)
│ └kaeri.csv (fichier csv préparé par vous-même)
│
└templates
├test.html
└test2.html
-Le calendrier suivant est préparé sous forme de fichier csv (exemple). (↓↓↓↓↓↓ Horaires des bus depuis l'arrêt près de chez vous jusqu'à la gare la plus proche) (↓↓↓↓↓↓ Horaires des bus de la gare la plus proche à l'arrêt près de chez vous)
-Créer jikoku.csv en traitant les horaires iki.csv de votre domicile à la gare la plus proche et les horaires kaeri.csv de votre gare la plus proche à votre domicile. -Créer un fichier csv_kakou.py pour créer jikoku.csv. -Si vous exécutez ce qui suit, un fichier jikoku.csv qui est une version traitée de iki.csv et kaeri.csv sera créé dans le répertoire assets. ・ Tout d'abord, le traitement de csv est terminé.
.py:csv_kakou.py
#iki.Traitement du csv
list = []
with open('assets/iki.csv',encoding='utf-8')as f:
#Processus de lecture ligne par ligne
for i in f:
columns = i.rstrip()
list.append(columns)
list2 = []
for i in list:
columns2 = i.split(',')
for ii in range(len(columns2)):
if ii != 0:
list2.append(columns2[0]+'Temps'+columns2[ii]+'Minutes')
list2.pop(0)
num = 1
with open('assets/jikoku.csv','w',encoding='utf-8')as f:
go_or_come = 'Aller'
for time in list2:
f.write(str(num) +','+time+','+str(go_or_come)+'\n')
num+=1
#kaeri.Traitement du csv
list = []
with open('assets/kaeri.csv',encoding='utf-8')as f:
#Processus de lecture ligne par ligne
for i in f:
columns = i.rstrip()
list.append(columns)
list2 = []
for i in list:
columns2 = i.split(',')
for ii in range(len(columns2)):
if ii != 0:
list2.append(columns2[0]+'Temps'+columns2[ii]+'Minutes')
list2.pop(0)
with open('assets/jikoku.csv','a',encoding='utf-8')as f:
go_or_come = 'Revenir'
for time in list2:
f.write(str(num) +','+time+','+str(go_or_come)+'\n')
num+=1
・ ↓↓↓↓↓↓↓ Le jikoku.csv créé dans le répertoire des actifs est le suivant (extrait partiel). Il y a 64 enregistrements au total.
Créez chacun dans le répertoire des actifs.
.py:database.py
#coding: utf-8
from sqlalchemy import create_engine
from sqlalchemy.orm import scoped_session,sessionmaker
from sqlalchemy.ext.declarative import declarative_base
import datetime
import os
database_file = os.path.join(os.path.abspath(os.path.dirname(__file__)),'data.db')
engine = create_engine('sqlite:///' + database_file,convert_unicode=True,echo=True)
db_session = scoped_session(
sessionmaker(
autocommit = False,
autoflush = False,
bind = engine
)
)
Base = declarative_base()
Base.query = db_session.query_property()
def init_db():
#Importer des modèles dans le dossier des actifs
import assets.models
Base.metadata.create_all(bind=engine)
.py:models.py
#coding: utf-8
from sqlalchemy import Column,Integer,String,Boolean,DateTime,Date,Text
from assets.database import Base
from datetime import datetime as dt
#Informations sur la table de base de données
class Data(Base):
#Paramètres de nom de table,Définir pour nommer les données
__tablename__ = "data"
#Définir les informations de colonne, définir unique sur False (ce qui signifie que la même valeur est acceptée)
#La clé primaire est requise lors de la recherche d'une ligne, généralement définie
id = Column(Integer,primary_key=True)
#Juger s'il faut partir ou revenir
go_or_come = Column(Text,unique=False)
#Numérotation séparée de la clé principale
num = Column(Integer,unique=False)
#Temps dans le calendrier
time = Column(Text,unique=False)
#timestamp
timestamp = Column(DateTime,unique=False)
#initialiser
def __init__(self,go_or_come=None,num=0,time=None,timestamp=None):
self.go_or_come = go_or_come
self.num = num
self.time = time
self.timestamp = timestamp
-Créer un fichier csv_to_sql.py qui lit les données csv et les écrit dans les données sql.
.py:csv_to_sql.py
from assets.database import db_session
from assets.models import Data
#Processus d'initialisation
from assets.database import init_db
init_db()
#Traitement pour écrire de CSV vers SQL
with open('assets/jikoku.csv',encoding='utf-8')as f:
for i in f:
columns = i.rstrip().split(',')
num = int(columns[0])#nombre de modèles.Puisqu'il est défini comme type int dans py, il a été fait de type int
time = columns[1]
go_or_come = columns[2]
row = Data(num=num,time=time,go_or_come=go_or_come)
db_session.add(row)
db_session.commit()
-Initier sql avec init_db (). -Après de_session.add, écrivez dans sql par db_session.commit. -Vérifiez s'il a été écrit correctement en sql. Accédez au répertoire des ressources et entrez ce qui suit pour passer en mode sqlite.
sqlite3 data.db
Si vous entrez ce qui suit dans sqlite,
select * from data;
Ce qui suit a été produit, et j'ai pu confirmer que les données ont été écrites dans SQL.
-Un fichier qui récupère l'heure spécifiée à partir de l'heure enregistrée dans la base de données sql. -Lorsque'going 'ou' returning 'est assigné à l'argument, la dernière heure de bus à partir de l'heure actuelle et l'heure de bus suivante sont extraites de sql et renvoyées comme valeur de retour.
.py:jikoku.py
from assets.database import db_session
from assets.models import Data
import datetime
def jikoku_search(route):
#Lire SQL
data = db_session.query(Data.num,Data.time,Data.go_or_come,).all()
#Obtenir la date et l'heure actuelles (type datetime)
date_todaytime = datetime.datetime.today()
#Convertissez ce qui précède en type str
str_todaytime = date_todaytime.strftime('%Y année%m mois%jour j%H heure%M minutes')
#De la date et de l'heure actuelles, seul ● année ● mois ● jour est acquis(type de date)
date = datetime.date.today()
#Convertissez ce qui précède en type str
str_date = date.strftime('%Y année%m mois%jour j')
#Définir des variables
bustime = ''
next_bustime = ''
#l'itinéraire classe les allers-retours
route = route
#Extraire l'heure de départ du dernier bus et l'heure de départ du prochain bus de sql
for i in data:
str_sql = i[1]
#Ajoutez la date et l'heure actuelles ● année ● mois ● jour à l'heure de sqr pour en faire "date et heure"
str_sql_rr = str_date + str_sql
#Convertissez ce qui précède en type datetime
date_sql_rr = datetime.datetime.strptime(str_sql_rr,'%Y année%m mois%jour j%H heure%M minutes')
#Obtenez la date et l'heure du départ le plus récent par rapport à la date et à l'heure actuelles
if date_sql_rr > date_todaytime and i[2]== route:#go_or_Si venir correspond à l'itinéraire, procédez comme suit
#Obtenez la différence entre la date et l'heure de départ du dernier bus et la date et l'heure actuelles
date_sabun = date_sql_rr-date_todaytime
#La différence de datetime est de type timedelta. Puisque le type timedelta ne peut pas être transformé en type str avec strftime, str()Fabriqué en type str
#le type de timedelta est 0:00:Comme il est 00 et que la différence se situe à moins d'une heure de l'horaire, les "minutes" sont extraites par découpage.
if str(date_sabun)[0:1] == "0":
bustime = 'Le prochain bus est'+str_sql_rr+'Départ pour.' + 'après' + str(date_sabun)[2:4] + 'Ce sont des minutes.'
else:
bustime = 'Le prochain bus est'+str_sql_rr+'Départ pour.' + 'après'+ str(date_sabun)[0:1] + 'temps' + str(date_sabun)[2:4] + 'Ce sont des minutes.'
#Obtenez le numéro de la date et de l'heure de départ du prochain bus
next_num = i[0]
#Obtenir l'heure de départ du prochain bus (traitement lorsqu'il y a le dernier bus mais que le prochain bus dépasse le dernier train)
try:
_next_bustime = db_session.query(Data.num,Data.time,Data.go_or_come).all()[next_num].time
#Ajoutez la date et l'heure actuelles ● année ● mois ● jour à l'heure de départ du prochain bus pour en faire «date et heure»
next_bustime = str_date + _next_bustime+'Départ pour.'
except:
next_bustime="C'est sur le dernier train."
#Traitement pour quitter l'instruction for après avoir obtenu l'heure du bus
break
#Traitement lorsque le dernier train est terminé pour le dernier bus et le bus suivant
else:
bustime="Le prochain bus est passé le dernier train."
next_bustime="Le prochain bus est également sur le dernier train."
return bustime,next_bustime
-Créez local_main.py pour vérifier que jikoku.py fonctionne correctement dans l'environnement local. -Tout d'abord, affichez test.html et lisez la base de données sql. -Depuis que "go" et "return" sont spécifiés dans test.html, les arguments pour "go" et "return" sont respectivement affectés à la méthode jikoku_search de jikoku.py et la valeur de retour est définie sur test2.html. revenir.
.py:local_main.py
from flask import Flask,request,render_template
from assets.database import db_session
from assets.models import Data
import jikoku_main as jm
app = Flask(__name__)
@app.route('/')
def test():
#Lire à partir de SQL
data = db_session.query(Data.num,Data.time,Data.go_or_come,).all()
return render_template('test.html',data=data)
@app.route('/iki')
def test2():
result1,result2 = jm.jikoku_search('Aller')
return render_template('test2.html',bustime=result1,next_bustime=result2)
@app.route('/kaeri')
def test3():
result1,result2 = jm.jikoku_search('Revenir')
return render_template('test2.html',bustime=result1,next_bustime=result2)
if __name__ == '__main__':
app.run(debug=True)
Créez chacun dans le répertoire des modèles comme suit.
.html:test.html
<!DOCTYPE html>
<html lang='ja'>
<head>
<meta charset='utf-8'>
<title>Jikokuhyou</title>
<style>body{padding:10px;}</style>
</head>
<body>
<form action='/iki' method='get'>
<button type='submit'>Aller</button>
</form>
<form action='/kaeri' method='get'>
<button type='submit'>Revenir</button>
</form>
</body>
</html>
.html:test2.html
<!DOCTYPE html>
<html lang='ja'>
<head>
<meta charset='utf-8'>
<title>Jikokuhyou</title>
<style>body{padding:10px;}</style>
</head>
<body>
{{'Le dernier bus est'+bustime}} <br>{{'Le prochain bus est'+next_bustime}}
<form action='/' method='get'>
<button type='submit'>Revenir</button>
</form>
</body>
</html>
.py:__init__.py
-D'abord, exécutez csv_to_sql.py pour initialiser la base de données, lisez de csv à sql et créez data.db. -Ensuite, exécutez local_main.py pour démarrer Flask et vérifiez-le avec un navigateur. ・ ↓↓↓↓↓↓↓ (Confirmation du navigateur) Appuyez sur le bouton «Go» ou «Return». ・ ↓↓↓↓↓↓↓ (confirmation du navigateur) Il a été affiché correctement (l'heure actuelle est 2:13)
-Depuis qu'il a été vérifié dans l'environnement local, il est finalement converti en LINE Bot en utilisant Heroku.
-Ajouter des répertoires et des fichiers comme indiqué ci-dessous. -Pour référence, ajoutez le data.db créé jusqu'à présent et jikoku.csv.
linebot_jikokuhyou
├csv_kakou.py
├csv_to_sql.py
├local_main.py
├jikoku_main.py
├main.py (supplémentaire)
├requirments.txt (supplémentaire)
├runtime.txt (supplémentaire)
├Procfile (supplémentaire)
├assets
│ ├database.py
│ ├models.py
│ ├data.db (fichiers créés jusqu'à présent)
│ ├__ini__.py
│ ├jikoku.csv (fichiers créés jusqu'à présent)
│ ├iki.csv
│ └kaeri.csv
│
└templates
├test.html
└test2.html
-Tout d'abord, utilisez line-sdk pour créer un fichier main.py qui notifie l'heure du bus. -Importez le module jikoku_main et utilisez la méthode jikoku_search de jikoku_main.
.py:main.py
from flask import Flask, request, abort
from linebot import (
LineBotApi, WebhookHandler
)
from linebot.exceptions import (
InvalidSignatureError
)
from linebot.models import (
MessageEvent, TextMessage, TextSendMessage,LocationMessage,ImageSendMessage
)
import os
import jikoku_main as jm
app = Flask(__name__)
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)
@app.route("/")
def hello_world():
return "hello world!"
@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'
@handler.add(MessageEvent, message=TextMessage)
def handle_message(event):
if 'Aller' in event.message.text:
result1,result2 = jm.jikoku_search('Aller')
line_bot_api.reply_message(
event.reply_token,
[
ImageSendMessage(original_content_url='https://www.photolibrary.jp/mhd5/img237/450-2012011014412960119.jpg',
preview_image_url='https://www.photolibrary.jp/mhd5/img237/450-2012011014412960119.jpg'),
TextSendMessage(text=result1),
TextSendMessage(text=result2)
]
)
if 'Revenir' in event.message.text:
result1,result2 = jm.jikoku_search('Revenir')
line_bot_api.reply_message(
event.reply_token,
[
ImageSendMessage
(original_content_url='https://www.photolibrary.jp/mhd5/img237/450-2012011014412960119.jpg',
preview_image_url='https://www.photolibrary.jp/mhd5/img237/450-2012011014412960119.jpg'),
TextSendMessage(text=result1),
TextSendMessage(text=result2)
]
)
if __name__ == "__main__":
port = int(os.getenv("PORT", 5000))
app.run(host="0.0.0.0", port=port)
・ Pour la structure générale de LINE Bot, reportez-vous à Utilisons LINE BOT (programme CallBack: reception). C'était. -Lors de l'envoi de plusieurs textes avec LINE Bot, utilisez une liste. -Lors de l'envoi d'images avec LINE Bot, il est limité au type https et jpeg. Je me suis référé au site suivant. [Introduction à Python] Envoyez des images et des phrases du compte officiel en utilisant l'API Line imagemap du bot de ligne python Envoyer l'image J'ai essayé d'utiliser line-bot-sdk-python ・ J'ai utilisé ici pour l'illustration du bus.
-Modifier database.py lors de l'utilisation de postgresql de Heroku. -Spécifiquement, décrivez le processus pour aller voir la variable d'environnement sur Heroku appelée environ et acquérir la base de données appelée DATABASE_URL. -L'URL de la destination de connexion est définie dans environ. De plus, en ajoutant ou, nous avons décidé de faire référence à sqlite comme une base de données dans l'environnement local. -Si vous êtes connecté à heroku, reportez-vous à l'url postgresql, et si vous n'êtes pas connecté, reportez-vous à sql.
.py:database.py
#coding: utf-8
#database.py/Fichier qui gère les paramètres initiaux de la base de données à utiliser, telle que sqlite
from sqlalchemy import create_engine
from sqlalchemy.orm import scoped_session,sessionmaker
from sqlalchemy.ext.declarative import declarative_base
import datetime
import os
#data_Base de données nommée, base de données.Où py est (os.path.dirname(__file__)), Avec un chemin absolu (os.path.abspath)、data_Enregistrer la base de données Enregistrez le chemin.
database_file = os.path.join(os.path.abspath(os.path.dirname(__file__)),'data.db')
#Utilisation de la base de données sqlite (engin)、database_données stockées dans un fichier_Utilisez db et émettez sqlite lors de l'exécution avec echo (echo=True)
#engine = create_engine('sqlite:///' + database_file,convert_unicode=True,echo=True)
engine = create_engine(os.environ.get('DATABASE_URL') or 'sqlite:///' + database_file,convert_unicode=True,echo=True)
db_session = scoped_session(
sessionmaker(
autocommit = False,
autoflush = False,
bind = engine
)
)
#declarative_Créer une instance de base
Base = declarative_base()
Base.query = db_session.query_property()
#Fonction d'initialisation de la base de données
def init_db():
#Importer des modèles dans le dossier des actifs
import assets.models
Base.metadata.create_all(bind=engine)
・ Tout d'abord, accédez aux développeurs LINE, enregistrez-vous et créez un nouveau canal (l'explication est omise) -Après avoir créé le canal, copiez la "chaîne de caractères du jeton d'accès" et la "chaîne de caractères secrète du canal" décrites dans LINE Developers. -Accédez à Heroku et créez une nouvelle application. -Initialisez git et associez-le à Heroku. -Définissez la "chaîne de caractères du jeton d'accès" et la "chaîne de caractères secrète du canal" aux variables d'environnement Heroku. -Par exemple, heroku config: set YOUR_CHANNEL_ACCESS_TOKEN = "Chaîne de caractères du jeton d'accès au canal" -a (nom de l'application)
heroku config:set YOUR_CHANNEL_ACCESS_TOKEN="Chaîne de jeton d'accès au canal" -a (nom de l'application)
heroku config:set YOUR_CHANNEL_SECRET="Chaîne secrète de canal" -a (nom de l'application)
Assurez-vous que les variables d'environnement sont correctement définies sur heroku.
heroku config
-Créer Procfile, runtime.txt, requirements.txt. -Créez runtime.txt après avoir vérifié votre propre version de python.
.txt:runtime.txt
python-3.8.2
web: python main.py
Décrivez requirements.txt en saisissant ce qui suit dans le terminal.
pip freeze > requirements.txt
-Déployer selon la procédure suivante. -Le nom du commit est le premier.
git add .
git commit -m'the-first'
git push heroku master
Heroku open
↓ ↓ ↓ ↓ ↓ Heroku ouvrir et vérifier avec un navigateur. Si "bonjour le monde!" S'affiche, le déploiement est terminé avec succès.
-Définissez la base de données Heroku postgresql et écrivez les données csv dans la base de données. -Définissez postgresql à partir de la ressource de l'application Heroku.
-Exécutez la commande bash pour que la commande puisse être saisie dans l'environnement Heroku. -Après cela, exécutez csv_to_sql.py. -En faisant cela, postgresql est initialisé et les données csv sont écrites dans postgresql.
heroku run bash
python3 csv_to_sql.py
Assurez-vous qu'il est écrit correctement. Entrez la commande suivante.
heroku pg:psql
Commande pour lister les données dans le tableau
select *from (nom de la table);
J'ai pu confirmer que ce qui suit était sorti et qu'il était correctement écrit dans postgresql.
Définissez l'URL du webhook LINE Developers et activez l'utilisation du webhook (les détails sont omis). Inscrivez-vous en tant qu'ami et lancez LINE Bot pour terminer.
Recommended Posts