3-2. Methodology : NUGU Backend proxy server with Flask
91923 ワード
1.nuguilサーバの完全なコード
python flaskを用いてREST APIを実装しnugu speakerからのpost要求のコードを処理する.
大きく分けてFunctional PartとFlash Partがあります.
from flask import Flask, request, jsonify
from flask_restful import Resource, Api
import json
import os
import requests
from pyproj import Proj
from pyproj import transform
import datetime
import time
import pandas as pd
from pandas import DataFrame
import telepot
from apscheduler.schedulers.background import BackgroundScheduler
#Function part
def location(): #find users location
url = 'https://www.googleapis.com/geolocation/v1/geolocate?key=AIzaSyDkx6muQn1Jz-y6hLOcTPVdYAhklm6WJQo'
data = {
'considerIp': True,
#'homeMobileCountryCode': 450,
#'homeMobileNetworkCode': 5,
#'radioType':'gsm',
#'carrier': "SKTelecom",
#"wifiAccessPoints":[{'macAddress':'40:DC:9D:06:EC:CA'}]
}
result = requests.post(url, data)
a=result.json()
lat=a['location']['lat'] # Y point
lng=a['location']['lng'] # X point
return lat,lng
def trans(lat,lng): #wgs84 -> tm128
WGS84 = { 'proj':'latlong', 'datum':'WGS84', 'ellps':'WGS84', }
TM128 = { 'proj':'tmerc', 'lat_0':'38N', 'lon_0':'128E', 'ellps':'bessel',
'x_0':'400000', 'y_0':'600000', 'k':'0.9999',
'towgs84':'-146.43,507.89,681.46'}
def wgs84_to_tm128(longitude, latitude):
return transform( Proj(**WGS84), Proj(**TM128), longitude, latitude )
x_point,y_point=wgs84_to_tm128(lng,lat)
return x_point,y_point
def ask_oil_type(ans):
if ans == "2번" : #경유
return "D047"
elif ans == "1번" : #휘발유
return "B027"
else :
return None
def browse(x_point,y_point,oil_type):
url = 'http://www.opinet.co.kr/api/aroundAll.do'
payload = {
"code" : "F886201116",
"out" : "json",
"x" : x_point,
"y" : y_point,
"radius" : "1000",
"prodcd" : oil_type ,
"sort" : "1"
}
result = requests.get(url,params=payload).json()
return result
def content():
global data_num
global oil_list
global title
global cost
data_num = len(oil_list["RESULT"]["OIL"]) #주유소 개수 확인
if (data_num == 1):
for i in range(0, 1):
title.append(oil_list["RESULT"]["OIL"][i]['OS_NM'])
elif (data_num >= 3):
for i in range(0, 3):
title.append(oil_list["RESULT"]["OIL"][i]['OS_NM'])
elif (data_num == 2):
for i in range(0,2):
title.append(oil_list["RESULT"]["OIL"][i]['OS_NM'])
content_num = data_num
if content_num == 0: #검색된 주유소가 0개인 경우
title.append("null")
cost.append("null")
elif content_num > 3: #검색된 주유소 3개 초과일 경우 차례대로 3개만 처리
for i in range(0, 3):
cost.append(oil_list['RESULT']['OIL'][i]['PRICE'])
else: #검색된 주유소가 2개 혹은 3개인 경우
for i in range(content_num):
cost.append(oil_list['RESULT']['OIL'][i]['PRICE'])
return title, cost
def action(c):
global data_num
keys=['number','title','cost']
arr=[]
if data_num == 0:
return arr
elif data_num == 1:
values = ["1", c[0], c[1]]
A = dict(zip(keys, values))
arr.append(A)
return arr
elif data_num == 2:
values_1 = ["1", c[0][0], c[1][0]]
values_2 = ["2", c[0][1], c[1][1]]
A = dict(zip(keys, values_1))
B = dict(zip(keys, values_2))
arr.append(A)
arr.append(B)
return arr
else:
values_1 = ["1", c[0][0], c[1][0]]
values_2 = ["2", c[0][1], c[1][1]]
values_3 = ["3", c[0][2], c[1][2]]
A = dict(zip(keys, values_1))
B = dict(zip(keys, values_2))
C = dict(zip(keys, values_3))
arr.append(A)
arr.append(B)
arr.append(C)
return arr
def make_response(result_list):
global select
global oil_type
response = {
"version": "2.0",
"resultCode": "OK",
"output": {
"COUNT": "0",
"STATION_INFORMATION": "",
}
}
result_len = len(result_list)
temp = ""
if result_len == 0:
return response
elif result_len>0:
response["output"]["COUNT"] = str(result_len)
if result_len == 1:
temp = str(result_list[0]["title"]) + "," + str(result_list[0]["cost"]) + "원"
temp = temp.replace(']','')
temp = temp.replace('[','')
temp = temp.replace("'",'')
response["output"]["STATION_INFORMATION"] = temp
else:
for i in range(result_len):
temp = temp + str(result_list[i]["title"]) + "," + str(result_list[i]["cost"]) + "원" + ","
temp = temp.replace("]","")
temp =temp.replace("[","")
temp = temp.replace("'","")
response["output"]["STATION_INFORMATION"] = temp
return response
#flask part
app = Flask(__name__)
api = Api(app)
class Getparams(Resource):
def post(self):
data = request.get_json()
print(data)
global select
global oil_type
ans = ""
if 'SELECT' in data['action']['parameters'].keys():
select = data['action']['parameters']['SELECT']['value']
if select == "1번" or select == "2번":
ans = select
if 'OIL_TYPE' in data['action']['parameters'].keys():
oil_type = data['action']['parameters']['OIL_TYPE']['value']
if oil_type == "경유":
ans = "2번"
elif oil_type == "휘발유":
ans = "1번"
a,b = location()
a,b = trans(a,b)
global oil_list
oil_list = browse(a,b,ask_oil_type(ans))
print(oil_list)
global data_num
global title
global cost
title = []
cost = []
result = action(content())
response = make_response(result)
if 'SELECT' in data['action']['parameters'].keys():
response["output"]["SELECT"] = select
if 'OIL_TYPE' in data['action']['parameters'].keys():
response["output"]["OIL_TYPE"] = oil_type
print(response)
return jsonify(response)
api.add_resource(Getparams,'/answer.lowprice','/answer.lowprice.diesel','/answer.lowprice.gasoline','/answer.lowprice.diesel.0','/answer.lowprice.diesel.1','/answer.lowprice.gasoline.0','/answer.lowprice.gasoline.1','/answer.lowprice.select.diesel','/answer.lowprice.select.diesel0','/answer.lowprice.select.diesel1','/answer.lowprice.select.gasoline','/answer.lowprice.select.gasoline0','/answer.lowprice.select.gasoline1')
if __name__ == "__main__":
app.run()
2.Nuguilサーバー機能部品
a.Geolocation APIを使用して現在位置を検索する
url
は、必要なGoogle Geolocation API URLおよびAPIキーを提供し、data
は、どのような情報要求に基づいて位置付けを要求するかに関するコードを提供する.nuguilはipに基づいてユーザ位置を追跡するため、'considerIp': True
を追加した.result 변수
は、地理的位置apiから取得された情報を受信し、それをjsonフォーマットに変換してa 변수
に格納する.ここで必要な値はa['location']['lat']
とa['location']['lng']
であり、それぞれy座標、x座標に対応する値である.これらの価格をそれぞれlat변수
とlng변수
に入れ、return lat,lng
を算出した.def location(): #find users location
url = 'https://www.googleapis.com/geolocation/v1/geolocate?key=AIzaSyDkx6muQn1Jz-y6hLOcTPVdYAhklm6WJQo'
data = {
'considerIp': True,
#'homeMobileCountryCode': 450,
#'homeMobileNetworkCode': 5,
#'radioType':'gsm',
#'carrier': "SKTelecom",
#"wifiAccessPoints":[{'macAddress':'40:DC:9D:06:EC:CA'}]
}
result = requests.post(url, data)
a=result.json()
lat=a['location']['lat'] # Y point
lng=a['location']['lng'] # X point
return lat,lng
現在、地理的位置を使用してユーザーの位置を特定することはできません.これは個人情報の問題でnugu speaker側の承認を得てこそ使える機能です.実行するために、コードを完了するために任意の座標を追加することにしました.(欠陥部分を参照)b.WGS 84フォーマットをオフィスネットワークのTM 128フォーマットに変換する
座標変換に関する関数を含むPyprojパッケージを用いた.parameterとして受け取ったWGS 84形式のlat,lng値をTM 128形式に変換し,
x_point
,y_point
変数を入れて返却する.def trans(lat,lng): #wgs84 -> tm128
WGS84 = { 'proj':'latlong', 'datum':'WGS84', 'ellps':'WGS84', }
TM128 = { 'proj':'tmerc', 'lat_0':'38N', 'lon_0':'128E', 'ellps':'bessel',
'x_0':'400000', 'y_0':'600000', 'k':'0.9999',
'towgs84':'-146.43,507.89,681.46'}
def wgs84_to_tm128(longitude, latitude):
return transform( Proj(**WGS84), Proj(**TM128), longitude, latitude )
x_point,y_point=wgs84_to_tm128(lng,lat)
return x_point,y_point
c.オフィスネットワークに変換する「prodcd」コード
この関数はnugu speakerからpostリクエスト受信の情報をパラメータとして受信し、officeapiを使用する場合に入力する「prodcd」コードに変換します.
def ask_oil_type(ans):
if ans == "2번" : #경유
return "D047"
elif ans == "1번" : #휘발유
return "B027"
else :
return None
d.半径5キロ以内のガソリンスタンドは、最低価格で並べ替えます
この関数はofficetapiを用いて
x_point
とy_point
とoil_type
をパラメータとし,request因子を加えて半径5キロ以内で最も安いガソリンスタンドのソート情報を対応する位置から取得する.def browse(x_point,y_point,oil_type):
url = 'http://www.opinet.co.kr/api/aroundAll.do'
payload = {
"code" : "F886201116",
"out" : "json",
"x" : x_point,
"y" : y_point,
"radius" : "5000",
"prodcd" : oil_type ,
"sort" : "1"
}
result = requests.get(url,params=payload).json()
return result
e.検索したガソリンスタンド数を確認し、最大3つの結果値を提供する
この関数は、brows関数の戻り値で先にリストされたガソリンスタンドのリストをglobalタイプの
oil_list
に格納し、いくつのガソリンスタンドがあるかを計算し、globalタイプのdata_num변수
に格納し、最大3つのガソリンスタンドの名前と価格をglobalタイプのtitle
、cost
にそれぞれ格納する.def content():
global data_num
global oil_list
global title
global cost
data_num = len(oil_list["RESULT"]["OIL"]) #주유소 개수 확인
if (data_num == 1):
for i in range(0, 1):
title.append(oil_list["RESULT"]["OIL"][i]['OS_NM'])
elif (data_num >= 3):
for i in range(0, 3):
title.append(oil_list["RESULT"]["OIL"][i]['OS_NM'])
elif (data_num == 2):
for i in range(0,2):
title.append(oil_list["RESULT"]["OIL"][i]['OS_NM'])
content_num = data_num
if content_num == 0: #검색된 주유소가 0개인 경우
title.append("null")
cost.append("null")
elif content_num > 3: #검색된 주유소 3개 초과일 경우 차례대로 3개만 처리
for i in range(0, 3):
cost.append(oil_list['RESULT']['OIL'][i]['PRICE'])
else: #검색된 주유소가 2개 혹은 3개인 경우
for i in range(content_num):
cost.append(oil_list['RESULT']['OIL'][i]['PRICE'])
return title, cost
f.結果値をディックシーケンスリストに入れる
Action(c)関数は、content関数のコールバック
**title**
、**cost**
のリストをcのパラメータとして受け入れ、**keys=['number','title','cost']**
をキー値とするdickshernerにc値を入れてコールバックする関数である.def action(c):
global data_num
keys=['number','title','cost']
arr=[]
if data_num == 0:
return arr
elif data_num == 1:
values = ["1", c[0], c[1]]
A = dict(zip(keys, values))
arr.append(A)
return arr
elif data_num == 2:
values_1 = ["1", c[0][0], c[1][0]]
values_2 = ["2", c[0][1], c[1][1]]
A = dict(zip(keys, values_1))
B = dict(zip(keys, values_2))
arr.append(A)
arr.append(B)
return arr
else:
values_1 = ["1", c[0][0], c[1][0]]
values_2 = ["2", c[0][1], c[1][1]]
values_3 = ["3", c[0][2], c[1][2]]
A = dict(zip(keys, values_1))
B = dict(zip(keys, values_2))
C = dict(zip(keys, values_3))
arr.append(A)
arr.append(B)
arr.append(C)
return arr
G.NUGU PlayBuilderの返信フォーマットを返します
make response(result list)関数は、action関数が返す戻り値が返すディックシーケンスリストを受信し、nugu speakerがpost要求を受信した後にnugu speakerに必要な応答ブロックデータ型のディックシーケンスを返す関数である.
response["output"]["COUNT"]
はガソリンスタンドの総数を含み、response["output"]["STATION_INFORMATION"]
はガソリンスタンドの情報を含む.「~ガソリンスタンド、~一、~ガソリンスタンド、~一」など、最大3つのガソリンスタンドの情報を文字列でリストします.「~ガソリンスタンド、~一、~ガソリンスタンド、~一」という形式を用いるのは、nuguspeakerでガソリンスタンド情報を後で発言する際に、簡単な方法で途切れることなく情報を発信できるためである.
def make_response(result_list):
global select
global oil_type
response = {
"version": "2.0",
"resultCode": "OK",
"output": {
"COUNT": "0",
"STATION_INFORMATION": "",
}
}
result_len = len(result_list)
temp = ""
if result_len == 0:
return response
elif result_len>0:
response["output"]["COUNT"] = str(result_len)
if result_len == 1:
temp = str(result_list[0]["title"]) + "," + str(result_list[0]["cost"]) + "원"
temp = temp.replace(']','')
temp = temp.replace('[','')
temp = temp.replace("'",'')
response["output"]["STATION_INFORMATION"] = temp
else:
for i in range(result_len):
temp = temp + str(result_list[i]["title"]) + "," + str(result_list[i]["cost"]) + "원" + ","
temp = temp.replace("]","")
temp =temp.replace("[","")
temp = temp.replace("'","")
response["output"]["STATION_INFORMATION"] = temp
return response
2.NuguilサーバFlaskpart
まず、
data = request.get_json()
を介して受信されたrequest bodyには、サービスユーザがどのようなタイプの油を基準に最低価格のガソリンスタンドを検索したいかに関する情報が含まれている.この情報は、nugu playbuilderによって設定されたactionに従って、SELECT
またはOIL_TYPE
というパラメータに含まれ、そのうちの1つだけがrequest bodyに渡される.ifクエリ条件により、対応するキー値が存在するか否かを確認し、ans변수
にvalue値を入れる.browse関数でoiltypeをパラメータとする場合、関連コードに変換する関数ask oil type(ans)を使用するには、ans変数に入る値を「1番」または「2番」に変換する必要があります.したがって、data['action']['parameters']['OIL_TYPE']['value']
の値が「ガソリン」であれば「1号」、「ディーゼル」であれば「2号」となる.次に、location()関数で求めた位置情報を
a,b변수
に初期化し、その後、trans(a,b)関数でWGS 84形式のa,b値をTM 128形式に変換する.location()関数を使用してユーザーの位置を追跡する必要がありますが、個人情報の使用許可が得られていないため、どのスピーカーにも使用できません.したがって、次のコードは任意の座標値を使用して実行されます.
browse(a,b,ask oil type(ans))関数で求めたガソリンスタンドリストを
oil_list
に初期化し、oil_list
の値をaction(content()関数で所望のフォーマットに変換する.make response(result)関数でresponse body形式で情報を加工し、selectまたはoiltypeで入力した値をresponse bodyに追加します.
リクエストbodyとして受信したパラメータ値もresponse bodyに入力します.
app = Flask(__name__)
api = Api(app)
class Getparams(Resource):
def post(self):
data = request.get_json()
print(data)
global select
global oil_type
ans = ""
if 'SELECT' in data['action']['parameters'].keys():
select = data['action']['parameters']['SELECT']['value']
if select == "1번" or select == "2번":
ans = select
if 'OIL_TYPE' in data['action']['parameters'].keys():
oil_type = data['action']['parameters']['OIL_TYPE']['value']
if oil_type == "경유":
ans = "2번"
elif oil_type == "휘발유":
ans = "1번"
#a,b = location()
#print(a,b)
a,b = 37.585876,127.143135
a,b = trans(a,b)
global oil_list
oil_list = browse(a,b,ask_oil_type(ans))
print(oil_list)
global data_num
global title
global cost
title = []
cost = []
result = action(content())
response = make_response(result)
if 'SELECT' in data['action']['parameters'].keys():
response["output"]["SELECT"] = select
if 'OIL_TYPE' in data['action']['parameters'].keys():
response["output"]["OIL_TYPE"] = oil_type
print(response)
return jsonify(response)
api.add_resource(Getparams,'/answer.lowprice','/answer.lowprice.diesel','/answer.lowprice.gasoline','/answer.lowprice.diesel.0','/answer.lowprice.diesel.1','/answer.lowprice.gasoline.0','/answer.lowprice.gasoline.1','/answer.lowprice.select.diesel','/answer.lowprice.select.diesel0','/answer.lowprice.select.diesel1','/answer.lowprice.select.gasoline','/answer.lowprice.select.gasoline0','/answer.lowprice.select.gasoline1')
if __name__ == "__main__":
app.run()
Reference
この問題について(3-2. Methodology : NUGU Backend proxy server with Flask), 我々は、より多くの情報をここで見つけました https://velog.io/@nuguoil/3-2.-Methodology-NUGU-Backend-proxy-server-with-Flask-j08ehw2wテキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol