iT邦幫忙

第 11 屆 iT 邦幫忙鐵人賽

DAY 29
0
Modern Web

ML X 友廷等公車系列 第 29

Day 29 加量不加價但加分 - 加入最愛路線 & Custom ML API

架構圖

顯示加入/取消 最愛路線

  • Demo
  • Code
<html>
<script type="text/javascript" src="jquery-1.8.3.min.js"></script>
<body>
<img id="unlike" width:20px height:20px src="https://image.flaticon.com/icons/svg/1142/1142213.svg" width="20px" height="20px" value="0" onclick="toggle()" />
</body>
<script>
function toggle()
{
    var val = document.getElementById("unlike").getAttribute('value')
    console.log(val)
    if(val==0)
    {
       console.log("yyy")
       $("#unlike").attr("src","https://image.flaticon.com/icons/svg/1142/1142131.svg")
       $("#unlike").attr("value","1")
    }
    else
    {
       console.log("nnn")
       $("#unlike").attr("src","https://image.flaticon.com/icons/svg/1142/1142213.svg")
       $("#unlike").attr("value","0")      
    }
    
}
</script>
</html>

Firebase 登入/寫入/讀取

index.html

	<!-- firebase Login-->
	<script src="https://www.gstatic.com/firebasejs/6.5.0/firebase-app.js"></script>
    <script src="https://www.gstatic.com/firebasejs/5.9.1/firebase-auth.js"></script>
    <script src="https://www.gstatic.com/firebasejs/3.1.0/firebase-database.js"></script>

add login button

// Google 登入按鈕 //
var customControl3 = L.Control.extend({
	options: {
		position: 'topleft'
	},
	onAdd: function(map) {
		var container = L.DomUtil.create('div', 'leaflet-bar leaflet-control leaflet-control-custom');
		container.style.backgroundColor = 'white';
		container.style.backgroundImage = "url(data:image/png;base64,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)";
		container.style.backgroundSize = "50px 50px";
		container.style.width = '50px';
		container.style.height = '50px';
		container.onclick = function() {
		   console.log("Google Login")
		   openPopup()
		}
		return container;
	}
});
mymap.addControl(new customControl3());

變數宣告

var UID = ""              // 使用者ID
var userName = ""         // 使用者名稱
var Today=new Date();     
fb_login_status = 0       // 0 為還未登入

初始化 firebase

var firebaseConfig = {
	apiKey: "",
	authDomain: "",
	databaseURL: "",
	projectId: "fb-login-df75e",
	storageBucket: "",
	messagingSenderId: "",
	appId: ""
};
// Initialize Firebase
firebase.initializeApp(firebaseConfig);

Google 第三方彈出視窗

function openPopup()
{
	// var provider = new firebase.auth.GoogleAuthProvider()
	var provider = new firebase.auth.GoogleAuthProvider()
	
	firebase.auth().signInWithPopup(provider).then(function(result) {
	console.log('test') // 未登入 => 不會跑這裡 
	// This gives you a Facebook Access Token. You can use it to access the Facebook API.
	var token = result.credential.accessToken;
	// The signed-in user info.
	var user = result.user;
	console.log(user)
	console.log(user.displayName) // 使用者名稱
    userName = user.displayName
	console.log(user.uid)         // uid
	UID = user.uid
	console.log(typeof(user.displayName))
	console.log(typeof(user.uid))

	if(user.displayName!="" && user.uid!="")
	{
		fb_login_status = 1 
		console.log('Login Status:',fb_login_status)
	}

	// writeUserData(user.displayName,user.uid)

	// ...
	}).catch(function(error) {
	// Handle Errors here.
	var errorCode = error.code;
	var errorMessage = error.message;
	// The email of the user's account used.
	var email = error.email;
	// The firebase.auth.AuthCredential type that was used.
	var credential = error.credential;
	// ...
	});
}

firebase - write
依據 uid 寫入個人最愛清單

function writeUserData(name,uid) {
	console.log('name:',name)
	console.log('id',uid)
	loginTime =  Today.getFullYear().toString() +'/'+ (Today.getMonth()+1).toString() +'/' +Today.getDate().toString()
    console.log('UID',UID)
	var database = firebase.database().ref("users/"+UID)
	favo_list = JSON.stringify(loved)
	console.log(database)
	doc = {
	  'Name' : userName,	
	  'LoginTime' : loginTime,
	  'favorite_list': name
	}
	database.set(doc)
}

firebase - read
如果是第一次登入 不會進行讀取

            try{
                database.on('value',function(data){
                    console.log('Check')
                    favorite_list = data.val().favorite_list
                    Object.entries(loved).forEach(([key, value]) => { 
                        if(favorite_list.hasOwnProperty(key))
                        {
                            // match_count = match_count + 1
                            loved[key] = favorite_list[key]
                            //console.log(loved[key])
                            
                        }
                    });
                    OriginList = "";
                    part1="";
                    part2="";
                    Data = dataX
                    for (var i = 0; i < Data.length; i++) {
                        // update 
                        if (loved[Data[i].SubRoutes[0].SubRouteName.Zh_tw] == 0)
                        {
                                path = "https://image.flaticon.com/icons/svg/1142/1142213.svg"
                                valueX = 0
                                part1 += `
                                    <img id="${ Data[i].SubRoutes[0].SubRouteName.Zh_tw}" width:20px height:20px src="${path}" width="20px" height="20px" value="${valueX}" onclick="toggle('${ Data[i].SubRoutes[0].SubRouteName.Zh_tw}')" />
                                    <li class="card" title="路線詳情" onclick=updatemap('${ Data[i].SubRoutes[0].SubRouteName.Zh_tw}')>
                                    <p class="bus-way">${ Data[i].SubRoutes[0].Headsign }</p>
                                    <p class="bus-num">${ Data[i].SubRoutes[0].SubRouteName.Zh_tw}</p>
                                </li>`
                       }
                       else
                       {
                                path = "https://image.flaticon.com/icons/svg/1142/1142131.svg"
                                valueX = 1
                                part2 += `
                                    <img id="${ Data[i].SubRoutes[0].SubRouteName.Zh_tw}" width:20px height:20px src="${path}" width="20px" height="20px" value="${valueX}" onclick="toggle('${ Data[i].SubRoutes[0].SubRouteName.Zh_tw}')" />
                                    <li class="card" title="路線詳情" onclick=updatemap('${ Data[i].SubRoutes[0].SubRouteName.Zh_tw}')>
                                    <p class="bus-way">${ Data[i].SubRoutes[0].Headsign }</p>
                                    <p class="bus-num">${ Data[i].SubRoutes[0].SubRouteName.Zh_tw}</p>
                                </li>`
                       }
                    }
                   OriginList = part2+part1
                   list.innerHTML = OriginList;   
                    //
                })
            }
            catch(e)
            {
                console.log('第一次登入')
            }

打造首支 Custom ML API

運行於Google Cloud Platform

  • 定時收集資料 - collect_data2.py
from hashlib import sha1
import hmac
from wsgiref.handlers import format_date_time
from datetime import datetime
from time import mktime
import base64
from requests import request
from pprint import pprint
import json

import smtplib
from email.mime.text import MIMEText

app_id = ''
app_key = ''

class Auth():

    def __init__(self, app_id, app_key):
        self.app_id = app_id
        self.app_key = app_key

    def get_auth_header(self):
        xdate = format_date_time(mktime(datetime.now().timetuple()))
        hashed = hmac.new(self.app_key.encode('utf8'), ('x-date: ' + xdate).encode('utf8'), sha1)
        signature = base64.b64encode(hashed.digest()).decode()

        authorization = 'hmac username="' + self.app_id + '", ' + \
                        'algorithm="hmac-sha1", ' + \
                        'headers="x-date", ' + \
                        'signature="' + signature + '"'
        return {
            'Authorization': authorization,
            'x-date': format_date_time(mktime(datetime.now().timetuple())),
            'Accept - Encoding': 'gzip'
        }


if __name__ == '__main__':
    a = Auth(app_id, app_key)
    response = request('get', "https://ptx.transportdata.tw/MOTC/v2/Bus/RealTimeByFrequency/InterCity/9018?$filter=Direction%20eq%20'0'&$top=150&$format=JSON", headers= a.get_auth_header())
    
    str = str(response.content,'utf-8')
   
    jsonValue = json.loads(str)
    
    listA  = []
    try:
        #將已有的讀出來
        with open('/home/turningpoint1125/record2.json' , 'r') as reader:
            jf = json.loads(reader.read())
        #print(jf)
    
        for item in jsonValue:
            mydict = {}
            mydict['GPSTime'] = item['GPSTime']
            mydict['Lat'] = item['BusPosition']['PositionLat']
            mydict['Lon'] = item['BusPosition']['PositionLon']
            mydict['Speed'] = item['Speed']
            print(mydict)
            #jf.append(mydict)
            if item['PlateNumb'] in jf:
                jf[item['PlateNumb']].append(mydict)
            else:
                jf[item['PlateNumb']] = list()
                jf[item['PlateNumb']].append(mydict)            
            #listA.append(mydict)
        with open("/home/turningpoint1125/record2.json","w+") as dump_f:
            json.dump(jf,dump_f,indent=4)       
    except:
        gmail_user = 'turningpoint1125@gmail.com'
        gmail_password = 'wisdom5678' # your gmail password

        msg = MIMEText('Programmer No Life')
        msg['Subject'] = 'GCP運行情況'
        msg['From'] = 'turningpoint1125@gmail.com'
        msg['To'] = 'turningpoint1125@gmail.com'

        server = smtplib.SMTP_SSL('smtp.gmail.com', 465)
        server.ehlo()
        server.login(gmail_user, gmail_password)
        server.send_message(msg)
        server.quit()

        print('Email sent!')        
  • 選模型
  • 處理資料 - cal_fit_2.py
#載入收集來的資料
import json
with open('/home/turningpoint1125/record2.json','r') as load_f:
    load_dict = json.load(load_f)

#print(load_dict) #測試是否載入資料成功

#計算其他需要的特徵值 - distance,time

#print(len(load_dict)) #共14台 負責跑台中
GPSTime = []
Lat     = []
Lon     = []
Speed   = []

train_number = 0
for key in load_dict:
    #print(load_dict[key]) #[{第一筆資料},{第2筆資料},...]...[]
    #print(load_dict[key][0])
    #input('Stop')
    
    for item in load_dict[key]:
        if len(item) == 4 :
            #print(item['Lat']) #每一筆資料的距離(target)的依據 #Lat
            #print(item['Lon']) #每一筆資料的距離(target)的依據 #Lon
            GPSTime.append(item['GPSTime'])
            Lat.append(item['Lat'])
            Lon.append(item['Lon'])
            Speed.append(item['Speed'])
            train_number = train_number+1        
        

#計算 label
##前備工具(1):計算點點距離
import math
def getDistance(latA, lonA, latB, lonB):  
    ra = 6378140  # radius of equator: meter  
    rb = 6356755  # radius of polar: meter  
    flatten = (ra - rb) / ra  # Partial rate of the earth  
    # change angle to radians  
    radLatA = math.radians(latA)  
    radLonA = math.radians(lonA)  
    radLatB = math.radians(latB)  
    radLonB = math.radians(lonB)        
    pA = math.atan(rb / ra * math.tan(radLatA))  
    pB = math.atan(rb / ra * math.tan(radLatB))  
    x = math.acos(math.sin(pA) * math.sin(pB) + math.cos(pA) * math.cos(pB) * math.cos(radLonA - radLonB))   
    c1 = (math.sin(x) - x) * (math.sin(pA) + math.sin(pB))**2 / math.cos(x / 2)**2        
    c2 = (math.sin(x) + x) * (math.sin(pA) - math.sin(pB))**2 / math.sin(x / 2)**2    
    dr = flatten / 8 * (c1 - c2)  
    distance = ra * (x + dr)    
    return distance
##前備工具(2):載入路線經緯度資料
with open('/home/turningpoint1125/save.json' , 'r') as reader:
    route = json.loads(reader.read())
##前備工具(3):載入距離對照資料
with open('/home/turningpoint1125/disTable.json' , 'r') as reader:
    distoNTCU = json.loads(reader.read())
#計算離target的大略距離(先)
add_dis = []
for i in range(train_number):
    min = 3000
    rd = -1     # 不列入train data
    for j in range(0,13):
        if(float(Lat[i])!=route[j][0] and float(Lon[i])!=route[j][1]):
            dis = getDistance(float(Lat[i]),float(Lon[i]),route[j][0],route[j][1])
        else:
            dis = 0        
        if dis < min :
            rd = distoNTCU[j]
            min = dis            
    add_dis.append(rd)
#print(add_dis)
#print(len(add_dis))
#計算距離TARGET 所花時間(label)(後)
from datetime import datetime
#from dateutil.parser import parse
ctime = {}
for i in range(train_number):
    
    flag = 0
    if  str(add_dis[i])=="26.136332967166602":
        ctime[i] = "0"    
        flag = 1
        j = i - 1        
    while flag == 1 and str(add_dis[j])!="26.136332967166602":
        if j==1 or str(add_dis[j])=="-1":
            break

        year = GPSTime[j][0:4]
        year = int(year)
        #print('year:',int(year))
        month = GPSTime[j][5:7]
        #print('month:',int(month))
        month = int(month)
        day  = GPSTime[j][8:10]
        #print('day:',int(day))
        day = int(day)
        hour = GPSTime[j][11:13]
        #print('hour:',int(hour))
        hour = int(hour)
        minu = GPSTime[j][14:16]
        minu = int(minu)
        #print('min:',minu)
        
        year2 = GPSTime[j+1][0:4]
        year2 = int(year2)
        month2 = GPSTime[j+1][5:7]
        month2 = int(month2)
        day2  = GPSTime[j+1][8:10]
        day2  = int(day2)
        hour2 = GPSTime[j+1][11:13]
        hour2 = int(hour2)
        min2 = GPSTime[j+1][14:16]
        min2 = int(min2)        
        #input('stop')
        if (year2-year)==0 and (month2-month)==0 and (day2-day)==0 and (hour2-hour)==0 and (min2-minu)==5:
            #X = GPSTime[i][0:10]+' '+GPSTime[i][11:16]
            #X = datetime.strptime(str(X),"%Y-%m-%d %H:%M")
            #print(X)
            #print(A)
            #input('stop')
            X=GPSTime[i][14:16]
            X=int(X)
            
            #a = X - A 
            #a=str(a)
            ctime[j] = X - minu
            #print(a)
            #input('stop')
        else:
            break
        j = j - 1            
#print(ctime)

import time
today = time.strftime("%Y-%m-%d",time.localtime())
filename = '/home/turningpoint1125/'+today+'.csv' 
#將訓練資料儲成表格.csv
import csv
with open(filename,'w',newline='') as csvfile:
    writer = csv.writer(csvfile)
    writer.writerow(['GPSTime','Lat','Lon','disTO','Speed','Time'])
    for i in range(train_number):
        if i in ctime.keys():
            writer.writerow([GPSTime[i],Lat[i],Lon[i],add_dis[i],Speed[i],ctime[i]])
print('today data saved')
  • 定時訓練模型
import pandas as pd
from sklearn import linear_model

from sklearn import preprocessing                # 標準化1
from sklearn.preprocessing import MinMaxScaler   # 標準化2
scaler = MinMaxScaler()                          # 標準化2

import time
today = time.strftime("%Y-%m-%d",time.localtime())
filename = '/home/turningpoint1125/'+today+'.csv'
df = pd.read_csv(filename)
print('資料數量:',len(df))


#df_normalize = preprocessing.scale(df.drop(['Time','GPSTime'],axis='columns'))  # 標準化1
#df_normalize = scaler.fit_transform(df.drop(['Time','GPSTime'],axis='columns')) # 標準化2
#print(df_normalize)

reg = linear_model.LinearRegression()
reg.fit(df.drop(['Time','GPSTime'],axis='columns'),df.Time)
#reg.fit(df_normalize,df.Time)  # 標準化 1 2
#print('R^2:',reg.score(df_normalize,df.Time))

print('R^2:',reg.score(df.drop(['Time','GPSTime'],axis='columns'),df.Time))
print('weight:',reg.coef_ )
print('bias',reg.intercept_ )

import csv
with open('/home/turningpoint1125/daily_log.csv','a',encoding='utf8',newline='') as fd :
    writer = csv.writer(fd)
    writer.writerow([float(reg.coef_[0:1]),float(reg.coef_[1:2]),float(reg.coef_[2:3]),float(reg.coef_[3:4]),float(reg.intercept_ ),float(reg.score(df.drop(['Time','GPSTime'],axis='columns'),df.Time))])
  • 自動回報準確率、weights、bias 並記錄
import smtplib
from email.mime.text import MIMEText
gmail_user = ''
gmail_password = '' # your gmail password

LatW =  str(reg.coef_[0:1])
LonW =  str(reg.coef_[1:2])
DisW =  str(reg.coef_[2:3])
SpeW =  str(reg.coef_[3:4])
context = 'Lat: '+ LatW + '\n' + 'Lon: '+LonW+'\n'+'Dis: '+DisW+'\n'+'Speed: '+SpeW+'\n'+'Bias: '+str(reg.intercept_)+'\n'+'R^2: '+str(reg.score(df.drop(['Time','GPSTime'],axis='columns'),df.Time))+'\n'
 
msg = MIMEText(context)
msg['Subject'] = 'Good Night!'
msg['From'] = 'turningpoint1125@gmail.com'
msg['To'] = 'turningpoint1125@gmail.com'

server = smtplib.SMTP_SSL('smtp.gmail.com', 465)
server.ehlo()
server.login(gmail_user, gmail_password)
server.send_message(msg)
server.quit()

print('Email sent!')  

背景執行flask app

  • Python Flask app on Google Cloud Platform
import flask
from flask import jsonify

app = flask.Flask(__name__)
app.config["DEBUG"] = True

# load data - daily_log.csv 最後一筆 # 
import csv
with open('/home/turningpoint1125/daily_log.csv','r') as csvfile:
    rows = csv.reader(csvfile)
    count = 0 
    for row in rows:
        Lat = row[0]
        Lon = row[1]
        Dis = row[2]
        Speed = row[3]
        Bias = row[4]
        r2   = row[5]
        count = count + 1
print(count)        

import datetime

x = datetime.datetime.now()
print(x.year)
print(x.month)
print(x.day)


response = {}
response['Lat'] = Lat
response['Lon'] = Lon
response['Dis'] = Dis
response['Speed'] = Speed
response['Bias'] = Bias
response['R2'] = r2
response['date'] = str(x.year) + '/' + str(x.month) + '/' + str(x.day)
response['Start'] = 'taichung'
response['End'] = 'NTCU'
body = [response]

@app.route("/")
def estimate_waitingtime():
    return jsonify(body)

if __name__ == '__main__':
    app.run(host='0.0.0.0',port=80)
    

Demo


YEAR-MONTH-DATE 為訓練資料

daily_log.csv 紀錄每天的訓練結果 - 四個特徵值的係數 加上一個bias 以及 決定係數 R^2

自動回報模型狀況 by Gmail

Customed for 台中車站 > 台中教育大學 - ML API

Application 可以 call 的到

其他 - CORS 解決方案

$.ajax({
	url: "https://bypasscors.herokuapp.com/api/?url=http://35.202.154.243/",
	type: 'GET',
	responseType:'text/javascript',
	error: function(e)
	{
		console.log(e)
	},
	success: function(e){
		console.log(e)
	}
})

Solution :
https://bypasscors.herokuapp.com/api/?url= + http網址

Ref Tech

Deploying Simple Flask App with Google Cloud Instance


上一篇
Day 28 AAA 生日快樂/ *F12 console
下一篇
Day 30 Cheers!
系列文
ML X 友廷等公車30

1 則留言

0
阿展展展
iT邦好手 1 級 ‧ 2020-02-13 08:31:54

密碼XDDDD

我要留言

立即登入留言