見出し画像

Strava/Withings Web API開発 - 開発編②(Strava)

5/19から、月間走行距離がStravaの無料プランで見れなくなりました。サブスクリプションモデルへの移行ということで、無料プランの機能を削って有料プランのサービスを拡充する、といった方向性に舵を切るようです。
そうすると今回に開発した機能は重宝するかもしれません。

開発は以下のリンクを参照しました。(最初は標準のStrava APIで開発してましたが、JSONデータ取得後の加工のところで躓いたので)このStravaIOではアクティビティデータから項目単位のデータ取り出しを容易に行うことができます。sladkovmさんに感謝!

(参考)Strava API v3(公式)

(参考)StravaIO(github - sladkovm/stravaio)

概要

OpenWeatherMapの際と同様に、詳細は上記リンクやソースコードを見て頂くとして、要点を以下に整理します。

データ取得(Strava)
・現時点の日時を取得
・現在の日時をもとに、先月初日のタイムスタンプを取得
・RefreshTokenを実行し、新しいAccessTokenを取得
・AccessTokenを使用してStravaIOを実行
・StravaIOの実行によりactivitiesデータを取得
(先月初日から現時点までのactivitiesデータ)

データ加工(Strava)
・activitiesデータから、start_date(トレーニング開始時刻)、
 distance(走行距離)をプロパティとして取得。
・activityデータのstart_dateをもとに、当月か前月かを判断。
 今月であれば、今月用の配列(listz)に格納(走行距離を加算)
 先月であれば、先月用の配列(listy)に格納(走行距離を加算)

グラフ表示
listyに先月の1日~31日の1日単位の走行距離、listzに今月の1日~今日の1日単位の走行距離を格納。
月間走行距離を累積和のグラフにするため、cumsum関数を使用。
    listsumy=listy.cumsum()
 listsumz=listz.cumsum()

この結果得られたlistsumy、listsumzをプロットすると以下のように表示されます。

Stravaイメージ_Revised

先月と今月の月間走行距離もわかるようにラベルで表示しました。

ソースコード(Strava_RefreshToken.py)

import requests
data = {
 'client_id': 'クライアントID',
 'client_secret': 'クライアントシート',
 'grant_type': 'refresh_token',
 'refresh_token': 'リフレッシュトークン'
}
r = requests.post('https://www.strava.com/api/v3/oauth/token', data=data)
rtext = r.json()
atoken = rtext["access_token"]
path_w = 'Strava_AccessToken.txt'
with open(path_w, mode='w') as f:
   f.write(atoken)
with open(path_w) as f:
   print(f.read())

上記の通り、都度リフレッシュトークンで新しいアクセストークンを取得し、"Strava_AccessToken.txt"に保管します。

ソースコード(本体)

################################ 
###         Strava         ##### 
################################ 
###
## 現在の日時						dt_now
## 今月月初(日付)				stravaFirstDayofThisMonth
## 今月月初(タイムスタンプ)		stravaFirstDayofThisMonthTimestamp
## 現在の日付						today_d
## 現在の月						today_m
## 先月月初(日付)				stravaFirstDayofPrevMonth
## 先月月初(タイムスタンプ)		stravaFirstDayofPrevMonthTimestamp
## Stravaアクセストークンファイル	stravaAccessTokenFile
## Stravaクライアントデータ			client
## Stravaアスリートデータ			athlete
## Stravaアクティビティデータ		activities
## Stravaアクティビティ実施月		stravaActivityMonth
## Stravaアクティビティ実施日		stravaActivityDay

## 各種パッケージのインポート
from tkinter import *
import tkinter.ttk as ttk
import os
import requests
import json
import math
import datetime
from dateutil.relativedelta import relativedelta
from stravaio import StravaIO
import pandas as pd 
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.font_manager import FontProperties
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg

##################################    メイン処理    ##################################
## メインウィンドウ作成
root = Tk()

## メインウィンドウサイズ
root.geometry("1024x768")

## フレームを作成
frame1=Frame(root, bg="white")    ##グラフ表示用フレーム

## ジオメトリマネージャーで各項目を表示
frame1.pack(fill='both',expand=True)

## レイアウト調整(横幅を画面に合わせる)
frame1.columnconfigure(0, weight=1)  ##frame1は1項目なので引数は0

## メインウィンドウを閉じる
def wm_close():
   root.destroy()

## Closeボタン(X)
btn=Button(root, text=" X ", font=('', 16), relief=FLAT, command=wm_close)

## 画面がリサイズされたとき
def change_size(event):
   ## ボタンの位置を右上に
   btn.place(x=root.winfo_width() - 60, y=14)
## 画面のリサイズをバインドする
root.bind('<Configure>', change_size)

## メインウィンドウの最大化、最前面表示
root.attributes("-fullscreen", "1")
root.attributes("-topmost", True)

########################    Stravaデータ取得/更新    ########################
## このスクリプトの絶対パス
scr_path = os.path.dirname(os.path.abspath(sys.argv[0]))

def stravaUpdate():
   ##### timestampから月初とその1か月前を取得 
   dt_now = datetime.datetime.now() ##現時点の日時を取得 (yyyy-mm-dd hh:mm:ss.mmmmmm)
   stravaFirstDayofThisMonth = dt_now.replace(day=1,hour=0,minute=0,second=0,microsecond=0) ##月初に置き換え	
   stravaFirstDayofThisMonthTimestamp= stravaFirstDayofThisMonth.timestamp()  ##timestampに変換

   ##今日:today_d
   today_d = dt_now.astimezone(datetime.timezone(datetime.timedelta(hours=+9))).day

   ##今月:today_m
   today_m =stravaFirstDayofThisMonth.month
   stravaFirstDayofPrevMonth = stravaFirstDayofThisMonth + relativedelta(months=-1) 
   stravaFirstDayofPrevMonthTimestamp = stravaFirstDayofPrevMonth.timestamp()  ##timestampに変換

   ## StravaのAccessTokenをRefreshする。(ファイルに保管)
   import Strava_RefreshToken

   ## StravaのAccessTokenをファイルから取得する。
   stravaAccessTokenFile = 'Strava_AccessToken.txt'
   with open(stravaAccessTokenFile, 'r') as fd:
       STRAVA_ACCESS_TOKEN = fd.readline().rstrip('\n')

   ## StravaIOを実行
   client = StravaIO(access_token=STRAVA_ACCESS_TOKEN)
   client.__dict__

   ## athleteデータを取得
   athlete = client.get_logged_in_athlete()
   
  ## activitiesデータを取得
   activities = client.get_logged_in_athlete_activities(after=int(stravaFirstDayofPrevMonthTimestamp))
   
   ## Listを初期化(Listx:日付、Listy:前月走行距離、Listz:今月走行距離)
   listx = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31]
   listy = np.array([0]*31, dtype = 'float')
   listz = np.array([0]*31, dtype = 'float')
   
  ##listyに先月の1日~31日の1日単位の走行距離、listzに今月の1日~今日の1日単位の走行距離を格納。
   to_plot = []
   for a in activities:
       stravaActivityMonth = a.start_date.astimezone(datetime.timezone(datetime.timedelta(hours=+9))).month
       stravaActivityDay = a.start_date.astimezone(datetime.timezone(datetime.timedelta(hours=+9))).day
       
       ##Activityが今月ならlistz(今月)の該当日に距離を加算
       if stravaActivityMonth == today_m:  
              listz[stravaActivityDay-1] = listz[stravaActivityDay-1] + a.distance/1000

       ##Activityが先月ならlisty(先月)の該当日に距離を加算
       else:  
              listy[stravaActivityDay-1] = listy[stravaActivityDay-1] + a.distance/1000
           
       s = client.get_activity_streams(a.id, athlete.api_response.id)
       if isinstance(s, pd.DataFrame): 
           _s = s
       else:
           s.store_locally()
           _s = s.to_dict()
       to_plot.append({
           'activity': a,
           'streams': s
       })

   ##月間走行距離を累積和のグラフにするため、cumsum関数を使用
   listsumy=listy.cumsum()
   listsumz=listz.cumsum()

   ##月間走行距離をラベルで表示
   thisMonthDistance = listsumz[today_d-1]
   prevMonthDistance = listsumy[30]
   prevMonthDistance2 = listsumy[today_d-1]


########################    Stravaグラフ作成    ########################

   ##表示エリアの設定
   fig = plt.Figure()
   ax1 = fig.add_subplot(111)
   ax1.plot(listx, listsumy, listsumz)

   fp = FontProperties(fname=r'C:\WINDOWS\Fonts\YuGothM.ttc', size=14)
   ax1.set_title(u'月間累積',fontproperties = fp)     ## タイトル名
   ax1.set_xlabel(u'日付',fontproperties = fp)       ## 横軸のラベル名
   ax1.set_ylabel(u'走行距離',fontproperties = fp)    ## 縦軸のラベル名
   ax1.set_ylim(0, 600)
   ax1.legend(['先月','今月'],bbox_to_anchor=(0, 1), loc='upper left', borderaxespad=1, fontsize=8,prop={"family":"MS Gothic"})
   
   canvas = FigureCanvasTkAgg(fig, master=root)
   canvas.draw()
   canvas.get_tk_widget().pack(in_=frame1,side=LEFT)

   ## ラベル作成
   lbl0 = Label(text='[今月走行距離]: ' + '{:.2f}'.format(thisMonthDistance) +' km',foreground='#000000', background='#ffffff', font=('Meiryo UI', '20', 'bold'))
   lbl1 = Label(text='[先月走行距離]: ' + '{:.2f}'.format(prevMonthDistance) +' km',foreground='#000000', background='#ffffff', font=('Meiryo UI', '20'))
   lbl2 = Label(text='[先月走行距離(同一日)]: ' + '{:.2f}'.format(prevMonthDistance2) +' km',foreground='#000000', background='#ffffff', font=('Meiryo UI', '20'))

   ## 配置
   lbl0.place(x=800,y=485)
   lbl1.place(x=800,y=525)
   lbl2.place(x=800,y=565)
   
## 初回起動
stravaUpdate()

## メインループ
root.mainloop()

<関連記事>
Strava/Withings Web API開発(天気と走行距離と体重をダッシュボードで表示してみた)
https://note.com/sanoatsu/n/nbe00ce35d3b1
Strava/Withings Web API開発 - 全体概要
https://note.com/sanoatsu/n/ncc074b9ce5f7
Strava/Withings Web API開発 - 準備編①
https://note.com/sanoatsu/n/n0104f85631cf
Strava/Withings Web API開発 - 準備編②
https://note.com/sanoatsu/n/n4d05a6f1cb2d
Strava/Withings Web API開発 - 準備編③
https://note.com/sanoatsu/n/nb60129601d54
Strava/Withings Web API開発 - 開発編①
https://note.com/sanoatsu/n/n69dd5ca2b560

Strava/Withings Web API開発 - Pythonリファレンス
Web API認証
https://note.com/sanoatsu/n/n98742c974b17
GUIレイアウト作成:Tkinter
https://note.com/sanoatsu/n/ncb830ae933dd/
日付・時刻処理:datetime
https://note.com/sanoatsu/n/n050ca16df6c7/
HTTPでのデータ取得:Requests
https://note.com/sanoatsu/n/n75e81bc2896a
データ格納:JSONと配列、辞書
https://note.com/sanoatsu/n/na76312571869/
Stravaのデータ処理:StravaIO
https://note.com/sanoatsu/n/n3d9593ff83c6/



よろしければサポートお願いします。