# Day 39 - Intermediate+ Capstone Part 1: Flight Deal Finder

**URL:** https://vip.studycamp.tw/t/day-39-intermediate-capstone-part-1-flight-deal-finder/1871
**Category:** cv組
**Tags:** python, 上課筆記
**Created:** [2021年七月13日 14:37 UTC](https://vip.studycamp.tw/t/day-39-intermediate-capstone-part-1-flight-deal-finder/1871 "2021-07-13T14:37:59Z")
**Posts on this page:** 4
**Page:** 1

<div class="post-metadata">

### Author: ![sky](https://vip.studycamp.tw/user_avatar/vip.studycamp.tw/sky/32/8098_2.png) [@sky](https://vip.studycamp.tw/u/sky)
#### Post date: [2021年七月13日 14:37 UTC](https://vip.studycamp.tw/t/day-39-intermediate-capstone-part-1-flight-deal-finder/1871/1 "2021-07-13T14:37:59Z")

</div>

本章由 Shadow 分享，筆記連結網址於此，由 Sky 取得同意後，整理如下。

> **[Notion – The all-in-one workspace for your notes, tasks, wikis, and databases.](https://www.notion.so/DAY039-855d434217ce485b9bb41dac2ce04aa6)**
>
> A tool that connects everyday work into one space. It gives you and your teams AI tools—search, writing, note-taking—inside an all-in-one, flexible workspace.

時間：2021年6月6日 20:00~20:30  
與會人員：Shadow, 玉米, Dot, Wayne, Yeh, Sky  
分享：Shadow

## 目標

Flight Deal Feal

每日傳送最低機票票價資訊，地點及時間取至使用者自行設置的 Google Sheet 資訊，最後將最低機票票價資訊以 Line Notify 通知使用者

## 參考資料

### Sheety

> **[Sheety - Turn your Google Sheet into an API](https://sheety.co/)**
>
> Turn any Google Sheet into an API instantly. Power websites, apps, or whatever you like, all from a spreadsheet.

### RapidAPI

[https://rapidapi.com/marketplace](https://rapidapi.com/marketplace)

> **[API Hub](https://rapidapi.com/skyscanner/api/skyscanner-flight-search)**
>
> Explore your APIs

### Skyscanner API

[https://skyscanner.github.io/slate/#browse-quotes](https://skyscanner.github.io/slate/#browse-quotes)

### LINE Notify

> **[LINE Notify](https://notify-bot.line.me/zh_TW/)**

說明文件：

> **[LINE Notify](https://notify-bot.line.me/doc/en/)**

## 流程及步驟

### step 1：建立一個Google sheet表單，可參考下圖:

![Untitled](https://vip.studycamp.tw/uploads/default/original/2X/7/7d5354bdb3bc635365185f44541c95d99679446b.png)

### step 2：到Sheety中連結Google Sheet

```auto
import requests
SHEET_USERNAME="your SHEET_USERNAME"
PROJECTNAME="your PROJECTNAME"
SHEETNAME="your SHEETNAME"

SHEETYURL=f'https://api.sheety.co/{SHEET_USERNAME}/{PROJECTNAME}/{SHEETNAME}'

def getShrrtyYUrl(SHEET_USERNAME,PROJECTNAME,SHEETNAME):
	return f'https://api.sheety.co/{SHEET_USERNAME}/{PROJECTNAME}/{SHEETNAME}'

def getFlightInformation(sheetyUrl=SHEETYURL):
	getInformation = requests.get(sheetyUrl)
	return getInformation.json()

def insertInformation( country, currency, locale, originPlace, destinationPlace, outboundPartialDate, inboundPartialDate='',sheetyUrl=SHEETYURL):
	if not country and currency and locale and originPlace and destinationPlace and outboundPartialDate:
		print('Please input required data')
		return False

	parameters ={
		SHEETNAME:{
			 "country":country,
			 "currency":currency,
			 "locale":locale,
			 "originPlace":originPlace,
			 "destinationPlace":destinationPlace,
			 "outboundPartialDate":outboundPartialDate,
			 "inboundPartialDate":inboundPartialDate,
			}
		}
	try:
		response = requests.post(sheetyUrl, json=parameters)
		return True
	except Exception as err:
		print('insert error：', err)
		return False

def updateInformation(objectID,sheetyUrl=SHEETYURL, **updatedata,):
	updateUrl=sheetyUrl+'/'+str(objectID)
	headers={"Content-Type" : "application/json"}
	parameters={
		SHEETNAME:
			updatedata

	}
	try:
		updateRes=requests.put(updateUrl, json=parameters, headers=headers)
		updateRes.raise_for_status()
		return True
	except Exception as err:
		print('update error：',err)
		return False

```

### step 3：搜尋航班

Skyscanner Flight Search

```auto
import requests

APIENDPOINT='https://partners.api.skyscanner.net/apiservices/'
APIKEY ='your apikey' #input your apikey

def getLocales(apiKey=APIKEY):
	locales_url=f'{APIENDPOINT}/reference/v1.0/locales?apiKey={apiKey}'
	locales_res=requests.get(locales_url)
	return locales_res.json()

def getCurrency(apiKey=APIKEY):
	currency_url=f"{APIENDPOINT}/reference/v1.0/currencies?apiKey={apiKey}"
	currency_res=requests.get(currency_url)
	return currency_res.json()

def getMarkent(locate,apiKey=APIKEY):
	market_url= f"{APIENDPOINT}/reference/v1.0/countries/{locate}?apiKey={apiKey}"
	market_res=requests.get(market_url)
	return market_res.json()

def getPlace(country,currency,locate,query,apiKey=APIKEY):
	place_url=f"{APIENDPOINT}/autosuggest/v1.0/{country}/{currency}/{locate}?query={query}&apiKey={apiKey}"
	place_res=requests.get(place_url)
	return place_res.json()

def getCheapestQuotes(country,currency,locale,originPlace,destinationPlace,outboundPartialDate,inboundPartialDate):
	cheapFlightUrl = f"https://skyscanner-skyscanner-flight-search-v1.p.rapidapi.com/apiservices/browsequotes/v1.0/" \
					 f"{country}/{currency}/{locale}/{originPlace}/{destinationPlace}/{outboundPartialDate}/{inboundPartialDate}"

	headers = {
		'x-rapidapi-key': "your rapidapi key",
		'x-rapidapi-host': "skyscanner-skyscanner-flight-search-v1.p.rapidapi.com"
	}

	response = requests.get(cheapFlightUrl, headers=headers)
	return response.json()

```

### step 4：Line Notify 通知

```auto
import requests
def lineNotifyMessage(token, msg):
	headers = {
		"Authorization": "Bearer " + token,
		"Content-Type" : "application/x-www-form-urlencoded"
	}

	parameters = {'message': msg }
	r = requests.post("https://notify-api.line.me/api/notify", headers = headers, params = parameters)
	return r.status_code

```

主程式 main.py

```auto
import Sheety
import SkyscannerFlightSearch
import LineNotify

def getCarriersName(cheapestQuote_json,_id):
	carriersData=cheapestQuote_json.get('Carriers')
	for carriers in carriersData:
		carriersName = carriers.get('Name') if _id == carriers.get('CarrierId') else ''
		if carriersName:
			return carriersName
	return 'Sorry! Not match airport'

def getPlaceName(cheapestQuote_json,_id):
	placeData = cheapestQuote_json.get('Places')
	for place in placeData:
		placeName = place.get('Name') if _id == place.get('PlaceId') else ''
		if placeName:
			return placeName
	return 'Sorry! Not match Place'

def getOutboundMeg(cheapestQuote_json,outboundData):
	outboundDepartureDate = outboundData.get('DepartureDate')
	outboundCarrierId = outboundData.get('CarrierIds')[0]
	outboundCarrierName = getCarriersName(cheapestQuote_json, outboundCarrierId)
	outboundPlaceOriginId = outboundData.get('OriginId')
	outboundPlaceOriginName = getPlaceName(cheapestQuote_json, outboundPlaceOriginId)
	outboundPlaceDestinationId = outboundData.get('DestinationId')
	outboundPlaceDestinationName = getPlaceName(cheapestQuote_json, outboundPlaceDestinationId)

	outboundMsg = '預計出國時間:' +outboundDepartureDate + '\n' \
				+ '班機:' + outboundCarrierName + '\n' \
				+ '搭機地點:' + outboundPlaceOriginName + '\n' \
				+ '抵達地點:' + outboundPlaceDestinationName + '\n'

	return outboundMsg

def getInboundMeg(cheapestQuote_json,inboundData):
	inboundDepartureDate = inboundData.get('DepartureDate')
	inboundCarrierId = inboundData.get('CarrierIds')[0]
	inboundCarrierName = getCarriersName(cheapestQuote_json, inboundCarrierId)
	inboundPlaceOriginId = inboundData.get('OriginId')
	inboundPlaceOriginName = getPlaceName(cheapestQuote_json, inboundPlaceOriginId)
	inboundPlaceDestinationId = inboundData.get('DestinationId')
	inboundPlaceDestinationName = getPlaceName(cheapestQuote_json, inboundPlaceDestinationId)

	inboundMsg = '預計回國時間:' + inboundDepartureDate + '\n' \
				  + '班機:' + inboundCarrierName + '\n' \
				  + '搭機地點:' + inboundPlaceOriginName + '\n' \
				  + '抵達地點:' + inboundPlaceDestinationName

	return inboundMsg

if __name__ == ' __main__':
	LINENOTIFY_TOKEN = 'MgQ7EoyyCv5JPacBcrZaYeyRSMCtQH8iXLUiDAz98ml'
	ourFlight = Sheety.getFlightInformation()

	for flight in ourFlight.get('flightInformation'):
		country = flight.get('country')
		currency = flight.get('currency')
		locale = flight.get('locale')
		originPlace = flight.get('originPlace')
		destinationPlace = flight.get('destinationPlace')
		outboundPartialDate = flight.get('outboundPartialDate')
		inboundPartialDate = flight.get('inboundPartialDate')

		cheapestQuote_json=SkyscannerFlightSearch.getCheapestQuotes(country,currency
						,locale,originPlace,destinationPlace,outboundPartialDate,inboundPartialDate)

		for quote in cheapestQuote_json.get('Quotes'):
			minPrice= quote.get('MinPrice')
			direct = quote.get('Direct')

			outboundMsg = getOutboundMeg(cheapestQuote_json,quote.get('OutboundLeg'))
			inboundMsg = getInboundMeg(cheapestQuote_json,quote.get('InboundLeg'))

			notifyMsg = '價錢:' + str(minPrice) +'\n' \
						'是否為直達:' + str(direct) + '\n' \
						+outboundMsg+inboundMsg

			LineNotify.lineNotifyMessage(LINENOTIFY_TOKEN,notifyMsg)

```

---

<div class="post-metadata">

### Author: ![Jeff33](https://vip.studycamp.tw/user_avatar/vip.studycamp.tw/jeff33/32/140_2.png) [@Jeff33](https://vip.studycamp.tw/u/Jeff33)
#### Post date: [2021年八月14日 01:06 UTC](https://vip.studycamp.tw/t/day-39-intermediate-capstone-part-1-flight-deal-finder/1871/2 "2021-08-14T01:06:49Z")

</div>

請問我執行程式 出現下列這段代碼，是哪裡出問題了，抱歉，我已經上網查過，也嘗試著自己解決，但真的找不出問題點，只好向大家求救，麻煩各位先進幫忙看看，先謝過各位了。

```auto
C:\Users\jeff\PycharmProjects\Day39_Skyscanner\venv\Scripts\python.exe C:/Users/jeff/PycharmProjects/Day39_Skyscanner/main.py
Traceback (most recent call last):
  File "C:\Users\jeff\PycharmProjects\Day39_Skyscanner\main.py", line 62, in <module>
    for flight in ourFlight.get('flightInformation') :
TypeError: 'NoneType' object is not iterable

Process finished with exit code 1

```

line 62:

```auto
if __name__ == ' __main__' :
    LINENOTIFY_TOKEN = 'XXXXXXXXXXXXXXXXXXXX' # 這段我隱藏沒秀出
    ourFlight = Sheety.getFlightInformation()

    for flight in ourFlight.get('flightInformation') :
        country = flight.get('country')
        currency = flight.get('currency')
        locale = flight.get('locale')
        originPlace = flight.get('originPlace')
        destinationPlace = flight.get('destinationPlace')
        outboundPartialDate = flight.get('outboundPartialDate')
        inboundPartialDate = flight.get('inboundPartialDate')

        cheapestQuote_json = SkyscannerFlightSearch.getCheapestQuotes(country, currency, locale,
                                                                      originPlace, destinationPlace,
                                                                      outboundPartialDate, inboundPartialDate)

        for quote in cheapestQuote_json.get('Quotes') :
            minPrice = quote.get('MinPrice')
            direct = quote.get('Direct')

            outboundMsg = getOutboundMeg(cheapestQuote_json, quote.get('OutboundLeg'))
            inboundMsg = getInboundMeg(cheapestQuote_json, quote.get('InboundLeg'))

            notifyMsg = '價錢:' + str(minPrice) + '\n' \
                                                '是否為直達:' + str(direct) + '\n' \
                        + outboundMsg + inboundMsg
            LineNotify.lineNotifyMessage(LINENOTIFY_TOKEN, notifyMsg)

```

---

<div class="post-metadata">

### Author: ![Shadow](https://vip.studycamp.tw/uploads/default/original/1X/8779b4e6de8c77e4d8c6f3c27e9df54eb400d69f.jpeg) [@Shadow](https://vip.studycamp.tw/u/Shadow)
#### Post date: [2021年八月15日 12:59 UTC](https://vip.studycamp.tw/t/day-39-intermediate-capstone-part-1-flight-deal-finder/1871/3 "2021-08-15T12:59:43Z")

</div>

Hi Jeff33:  
我覺得可能是與sheety的連結有關係，  
所以可能可以檢查看看以下幾個點：  
1.檢查與Sheety的url是否有誤  
2.檢查google sheet中的工作表是否有flightInformation的工作表

---

<div class="post-metadata">

### Author: ![Jeff33](https://vip.studycamp.tw/user_avatar/vip.studycamp.tw/jeff33/32/140_2.png) [@Jeff33](https://vip.studycamp.tw/u/Jeff33)
#### Post date: [2021年十月14日 13:46 UTC](https://vip.studycamp.tw/t/day-39-intermediate-capstone-part-1-flight-deal-finder/1871/4 "2021-10-14T13:46:11Z")

</div>

感謝Shadow大的協助，抱歉這麼晚才來感謝，因後來身邊有其他事，就先跳過沒再處理這個問題，當時打算繼續學下去，等功力深一點後，再回頭重溫可能就看得懂了。

今天回頭看自己的程式碼，已經有點太複雜看得很吃力，似乎是像您說的 google sheet中的工作表找不到flightInformation的工作表，不過可能需要回頭看老師的影片重溫，屆時再來驗證是不是這個問題所致，不過非常謝謝您的協助，讓我可以有查詢的方向，謝謝!
