2020/08/20

Google FitにCSVから体重をインポート

Nokia Withingsの体重計のデータは、WithingsのHealthMateアプリでGoogle Fitに取り込まれるはずだが、どうやってもアップロードがうまく行かなかった。

最近、HealthMate経由で取り込まれるようになっていたので、過去分をWithingsのWeb画面からダウンロードして、Google Fitに取り込んだ。

手でやるには多すぎる量だったので、ネットを探し、公開されていたスクリプトを改修して利用した。

使用したスクリプト ー motherapp / weight-csv-to-gfit

 https://github.com/motherapp/weight-csv-to-gfit

  1. GitHubからダウンロード。

  2. Pythonは2.7のようなので、2.7で「pip install -r requirements.txt」して必要なツールをダウンロード。

  3. httplib2で証明書の検証ができずにエラーが出るので、関係ありそうなところを最新化
  4. pip2.7 install --upgrade httplib2
    pip2.7 install --upgrade google-api-python-client


  5. 体重データは、weightフォルダの中にweights.csvという名前でCSVで保存する。
  6. 注意事項
    • 1行目はタイトル行、2行目以降は「日付,体重」でCSVにする。
    • 日付はyyyy-mm-dd hh:mm:ssの形式(タイムゾーンは後でスクリプトを改修)
    • 古い日付から順に並べる(2行目が最も古い日付で、最終行が一番新しい日付)
    • 体重はkgでOK(オリジナルスクリプトはポンドで表されていることを想定しているが、改修する)

  7. Google FitのAPIの準備をする。
  8. https://console.developers.google.com/apis/credentials にアクセス

  9. 利用規約画面が出てくる場合は、同意。

  10. インポートで使用するプロジェクトを作成する

  11. 「ライブラリ」メニューから「Fitness API」を検索、選択し、「有効にする」ボタンを押下。

  12. 「認証情報」メニューに移り、「同意画面を構成」ボタンを押下。OAuth同意画面が出るので「外部」で作成。アプリケーション名(お好みの名前)だけ入れれば作成できる。

  13. 再び「認証情報」メニューに戻り、「+認証情報を作成」から「OAuthクライアントID」を選択。「アプリケーションの種類」は「ウェブアプリケーション」、名前はお好みの名前、承認済みのJavaScript生成元は空欄のまま、承認済みのリダイレクトURLは認証時にリダイレクトされるURL(自分のブログなどがあれば、そこでよい)を登録して「作成」

  14. OAuthクライアントのIDとシークレットが生成されるので、メモを取る。

  15. 「認証情報」のページの戻るので、「+認証情報を作成」から「APIキー」を選択。キーが生成される際、「キーを制限」するように言われるので、「キーを制限」を押下。「アプリケーションの制限」で「IPアドレス」を選んで、自端末のグローバルIPを登録。「APIの制限」で「キーを制限」を選び、先程有効化したFitness APIを選択。保存を押下。

  16. GitHubからダウンロードして展開済みのフォルダに戻り、先程作成したキーを登録したファイルsecrets.ymlを作成する。
  17. client_id: OAuthのクライアントID
    client_secret: OAuthのクライアントシークレット
    redirect_uri: 同意画面で登録したリダイレクト先のURL
    fitness_api_key: APIキー
    project_id: OAuthのクライアントIDの冒頭の数字の羅列(ハイフンまで)
    
    ※ project_idは本来は、先に作ったプロジェクトのIDだと思われるが、エラーになる。調べたところ、OAuthのクライアントIDの冒頭の数字の羅列の部分を期待しているようだったので、そうしたら動くようになった。

  18. weight-csv-to-gfitスクリプトの改修
  19. diff -Naur google-fit-data/weight/import_weight_to_gfit.py google-fit-data_new/weight/import_weight_to_gfit.py
    --- google-fit-data/weight/import_weight_to_gfit.py	2020-08-20 00:41:21.688641000 +0900
    +++ google-fit-data_new/weight/import_weight_to_gfit.py	2020-08-19 23:59:34.181251400 +0900
    @@ -26,7 +26,7 @@
     # Redirect URI to google Fit, See Steps 3 above
     #REDIRECT_URI='https://developers.google.com/oauthplayground'
     REDIRECT_URI = secrets['redirect_uri']
    -PROJECT_ID = secrets['project_id']
    +PROJECT_ID = str(secrets['project_id'])
     
     # See scope here: https://developers.google.com/fit/rest/v1/authorization
     SCOPE = 'https://www.googleapis.com/auth/fitness.body.write'
    @@ -49,7 +49,7 @@
         # there must be a way to programatically get this, but this exercise doesn't need it ... yet...
         token = raw_input("Copy the token from URL and input here: ")
         cred = flow.step2_exchange(token)
    -    http = httplib2.Http()
    +    http = httplib2.Http(disable_ssl_certificate_validation=True)
         http = cred.authorize(http)
         fitness_service = build('fitness','v1', http=http, developerKey=API_KEY)
     
    @@ -66,9 +66,9 @@
             ),
             device=dict(
               type='scale',
    -          manufacturer='withings',
    -          model='smart-body-analyzer',
    -          uid='ws-50',
    +          manufacturer='fukatani.org',
    +          model='import-script',
    +          uid='script-1',
               version='1.0',
             )
           )
    @@ -85,14 +85,14 @@
     
         data_source_id = get_data_source_id(data_source)
     
    -    print 'datasourceID'
    -    print data_source_id
    +    print 'datasourceID: ' + data_source_id
         # Ensure datasource exists for the device.
         try:
             fitness_service.users().dataSources().get(
                 userId='me',
                 dataSourceId=data_source_id).execute()
         except HttpError, error:
    +        print 'Tried to get dataSources(), but got error: ' + str(error)
             if not 'DataSourceId not found' in str(error):
                 raise error
             # Doesn't exist, so create it.
    diff -Naur google-fit-data/weight/read_weight_csv.py google-fit-data_new/weight/read_weight_csv.py
    --- google-fit-data/weight/read_weight_csv.py	2020-08-20 00:41:21.689638400 +0900
    +++ google-fit-data_new/weight/read_weight_csv.py	2020-08-19 21:51:19.176378200 +0900
    @@ -5,8 +5,9 @@
     from dateutil import zoneinfo
     
     DAWN_TIME = datetime.datetime(1970, 1, 1, tzinfo=dateutil.tz.tzutc())
    -TIME_ZONE = zoneinfo.gettz("America/New_York")
    -POUNDS_PER_KILOGRAM = 2.20462
    +TIME_ZONE = zoneinfo.gettz("Asia/Tokyo")
    +#POUNDS_PER_KILOGRAM = 2.20462
    +POUNDS_PER_KILOGRAM = 1
     
     def nano(val):
       """Converts a number to nano (str)."""


  20. インポートコマンドの実行
  21. cd weight/
    python2.7 import_weight_to_gfit.py
    


  22. 表示されるURLに、ブラウザからアクセス。

  23. Googleにログインの画面がでるので、続行。権限付与の画面でも許可を行う。

  24. その後、リダイレクトされるので、リダイレクトされたときのURLを入手する。
  25. 以下のような形になっているはず。
    https://リダイレクト先URL/?code=4/3AG~中略~sU&scope=https://www.googleapis.com/auth/fitness.body.write
    この中でcode=以降から&scope=前までの文字列を、pythonスクリプトに渡してやる。

    なお、code=の後は「4/3」という文字列で始まるようだが、「/」がURLエンコードされて「4%2F3A」となっていることがあるので、その場合は「%2F」の部分を「/」に変更してやることを忘れずに。

  26. うまくいくと、インポートされたデータがずら~っと表示される。Google Fitにデータが反映されるには暫く掛かる(1時間以上かかるときもある)ので、少し待って、インポートされていることを確認する。

0 件のコメント: