【パワプロ君っぽい】Oura Ring APIを活用したヘルスデータ管理アプリの作成(前半)

Glide

近年、健康データのトラッキングと分析が重要性を増しています。今回は、健康管理とアプリ開発を融合したプロジェクトとして、パワプロ君風のヘルスデータ管理アプリを作成したので、具体的な作成方法をご紹介します。このアプリは、基本的な機能を無料で利用できる複数のツールを組み合わせて使っているので、ご興味ある方はぜひ試しに作ってみてください!
今回は、Oura Ring APIを使って取得した健康データを、Googleスプレッドシートに記録し、Glideを使用してユーザーフレンドリーなアプリケーションに変換します。

作りたいアプリのイメージ紹介

このアプリは、日々の健康データ(睡眠スコア、心拍数、消費カロリーなど)を視覚的に表示し、ユーザーが自分の健康状態を容易に把握できるようにすることを目的としています。僕のイメージするパワプロ君風の画面表示を用いることで、情報がより親しみやすくしたいと思っています。

今回使うツールの紹介

  • Googleスプレッドシート: Oura APIから取得したデータの記録と、Glideで表示するデータの整理のために使用。
  • Googleドライブ: スプレッドシートの保存と、Glideで表示する画像データ(背景の空画像、体調アイコンのGIF画像など)の保存先として使用。
  • Oura API(V2): リングで測定した健康データの取得に使用。
  • Glide: ノーコードでスプレッドシートのデータを基にしたアプリケーションを作成し、アプリケーションを限定した範囲内で公開。

具体的なアプリの作り方(前半)

ここからは、先ほど紹介した4つのツールを使って、実際にアプリを作成する手順をご紹介します。前半半分の今回の記事では、Oura APIから取得したヘルスデータをGoogleスプレッドシートに記載するところまでをご紹介します。

スプレッドシートを準備

まず始めに、Googleスプレッドシートを用意します。
作成したGoogleスプレッドシートの名前を任意の名前に変更しておきましょう。(※今回作成するアプリでは、スプレッドシート自体の名前はなんでもいいです!)

画像を準備

次に、作成するアプリで使用する画像を準備していきます。どんなアプリを作るかにもよりますが、今回はパワプロ君っぽい画面表示を作りたいので、下記のような画像をネットから探してきています。
欲しい画像がない時は、Canvaなどの無料のデザインツールを使って画像を作成してみましょう。

以下の動画のように、アプリに使いたい画像をGoogleドライブにアップロードします。
ここではひとまず、アップロードしたい画像またはフォルダをドラッグ&ドロップでGoogleドライブに入れるだけでOKです。

スプレッドシートを作成

では次に、先ほど作成したGoogleスプレッドシートの中身を作成していきます。
まずは「体調シート」「評価シート」を作っていきます。これらのシートはOura APIから取得したヘルスデータと、ヘルスデータの結果をもとに推定したストレス値やコンディションなどを記載するためのものです。(※この後作成するスクリプトでは、下記画像の空欄部分にOura APIから取得したデータを記載していくようになっているため、「各シートの名前」と「セルの位置(D2,F2が空欄など)」は、合わせておくことをオススメします。)

また、この次の記事で説明するGlideでは、「体調シート」「評価シート」の内容と連動した画面表示を作成していきます。(※GlideでGoogleスプレッドシートを読み込む際、1行目はラベルとして扱われるので、下記画像のように1行目はラベルを入れておいてください。色は何色でもいいです。)

体調シート↓

評価シート↓

次に下記の動画のように、先ほど作成してGoogleドライブにアップロードしていたそれぞれの画像のリンクをコピーして、「評価一覧」「体調一覧」と言うシートを作成し、それぞれA列に記載した評価、体調に対応するようにB列のセルに画像のリンクを貼り付けます。

評価一覧↓

体調一覧↓

これらのように、4つのシートを作成できたら、スプレッドシートの準備は完了です。

スクリプトを作成

次は、先ほど作成した「体調シート」と「評価シート」の表の空欄に、Oura APIから取得したヘルスデータを記載するスクリプトを作成していきます。

まず下記のように、Apps Scriptを開きます。

スクリプトを記載するためのエディタを用意できたら、下記のコードを貼り付けてください。
繰り返す処理とかをうまく使えば、もっとコンパクトなスクリプトになると思いますが、僕は素人なので冗長的になりましたが、とりあえず動くのでOKということにしました。

実際に貼り付けるためのスクリプトはこちら↓

function main(){
  let token = '************' //ここにOuraAPI Tokenを記載※コード管理も注意
  let api = 'sleep'
  let today = new Date()
  let yesterday = new Date()
  yesterday.setDate(yesterday.getDate()-1)

  let parameters = {
    start_date: Utilities.formatDate(yesterday, 'JST', 'yyyy-MM-dd'),
    end_date: Utilities.formatDate(today, 'JST', 'yyyy-MM-dd')
  }
  let sleep = ConnectOuraAPI(api, parameters, token)
  let time = sleep.data[sleep.data.length - 1].total_sleep_duration
  time = time/60/60
  time = Math.round(time * 10) / 10;

  api = 'heartrate'
  let heartrate = ConnectOuraAPI(api, parameters, token)
  let bpm = heartrate.data[heartrate.data.length - 1].bpm
  let stressLevel = estimateStressLevel(bpm);
  stressLevel = Math.round(stressLevel * 10) / 10;

  api = 'daily_sleep'
  let dairySleep = ConnectOuraAPI(api, parameters, token)
  let dairySleepScore = dairySleep.data[0].score

  api = 'daily_activity'
  let activity = ConnectOuraAPI(api, parameters, token)
  let steps = activity.data[0].steps
  let sedentary_time = activity.data[0].sedentary_time
  sedentary_time = sedentary_time/60/60
  sedentary_time = Math.round(sedentary_time * 10) / 10;
  let total_calories = activity.data[0].total_calories

  api = 'daily_readiness'
  let readiness = ConnectOuraAPI(api, parameters, token)
  let temperature = readiness.data[0].temperature_deviation
  temperature = 36.8 + temperature
  
  var spreadsheetId = "************"; //スプレッドシートのIDを記載
  writeToSpreadsheet(spreadsheetId, "評価シート", [[dairySleepScore], [time], [stressLevel], [bpm], [total_calories], [steps], [sedentary_time], [temperature]]);

  var sleepScore1 = sleepscore();
  var sleeptimescore1 = sleeptimescore();
  var stressscore1 = stressscore();
  var bpmscore1 = bpmscore();
  var caloriesscore1 = caloriesscore();
  var stepsscore1 = stepsscore();
  var sedentaryscore1 = sedentaryscore();
  var temperaturescore1 = temperaturescore();

  let totalscore = sleepScore1 + sleeptimescore1 + stressscore1 + bpmscore1 + caloriesscore1 + stepsscore1 + sedentaryscore1 + temperaturescore1
  writeToSpreadsheet(spreadsheetId, "体調シート", [[totalscore]]);
  conditionscore();
}

function ConnectOuraAPI(api, parameters, token) {
  const baseUrl = 'https://api.ouraring.com/v2/usercollection/'
  let url = `${baseUrl}${api}?`
  for (let parameter in parameters){
    url += `${parameter}=${parameters[parameter]}&`
    if(Object.keys(parameters).slice(-1)[0] === parameter){
      url = url.slice(0, -1)
    }
  }
  let requestOptions = { 
    muteHttpExceptions: true,
    method: 'GET', 
    headers: {
      'Authorization': `Bearer ${token}`
    }
  }
  let res = UrlFetchApp.fetch(url, requestOptions).getContentText()
  return JSON.parse(res)
}

function writeToSpreadsheet(spreadsheetId, sheetName, data) {
  var spreadsheet = SpreadsheetApp.openById(spreadsheetId);
  var sheet = spreadsheet.getSheetByName(sheetName);
  var range = sheet.getRange("D2:D" + (1 + data.length));
  range.setValues(data);
}

function conditionscore() {
  var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
  var sheet1 = spreadsheet.getSheetByName("体調一覧");
  var sheet2 = spreadsheet.getSheetByName("体調シート");

  sheet2.getRange("F2").clearContent();

  var valueToCheck = sheet2.getRange("D2").getValue();
  if (valueToCheck >= 70) {
    var valueToCopy = sheet1.getRange("B2").getValue();
    sheet2.getRange("F2").setValue(valueToCopy);
  }else if (valueToCheck >= 60 && valueToCheck < 70) {
    var valueToCopy = sheet1.getRange("B3").getValue();
    sheet2.getRange("F2").setValue(valueToCopy);
  }else if (valueToCheck >= 50 && valueToCheck < 60) {
    var valueToCopy = sheet1.getRange("B4").getValue();
    sheet2.getRange("F2").setValue(valueToCopy);
  }else if (valueToCheck >= 40 && valueToCheck < 50) {
    var valueToCopy = sheet1.getRange("B5").getValue();
    sheet2.getRange("F2").setValue(valueToCopy);
  }else if (valueToCheck >= 30 && valueToCheck < 40) {
    var valueToCopy = sheet1.getRange("B6").getValue();
    sheet2.getRange("F2").setValue(valueToCopy);
  }else if (valueToCheck < 30) {
    var valueToCopy = sheet1.getRange("B7").getValue();
    sheet2.getRange("F2").setValue(valueToCopy);
  }
}

function sleepscore() {
  var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
  var sheet1 = spreadsheet.getSheetByName("評価一覧");
  var sheet2 = spreadsheet.getSheetByName("評価シート");
  var score;

  sheet2.getRange("F2").clearContent();

  var valueToCheck = sheet2.getRange("D2").getValue();
  if (valueToCheck >= 90) {
    var valueToCopy = sheet1.getRange("B2").getValue();
    sheet2.getRange("F2").setValue(valueToCopy);
    score = 10;
  }else if (valueToCheck >= 85 && valueToCheck < 90) {
    var valueToCopy = sheet1.getRange("B3").getValue();
    sheet2.getRange("F2").setValue(valueToCopy);
    score = 8;
  }else if (valueToCheck >= 80 && valueToCheck < 85) {
    var valueToCopy = sheet1.getRange("B4").getValue();
    sheet2.getRange("F2").setValue(valueToCopy);
    score = 6;
  }else if (valueToCheck >= 75 && valueToCheck < 80) {
    var valueToCopy = sheet1.getRange("B5").getValue();
    sheet2.getRange("F2").setValue(valueToCopy);
    score = 5;
  }else if (valueToCheck >= 70 && valueToCheck < 75) {
    var valueToCopy = sheet1.getRange("B6").getValue();
    sheet2.getRange("F2").setValue(valueToCopy);
    score = 4;
  }else if (valueToCheck >= 60 && valueToCheck < 70) {
    var valueToCopy = sheet1.getRange("B7").getValue();
    sheet2.getRange("F2").setValue(valueToCopy);
    score = 3;
  }else if (valueToCheck >= 50 && valueToCheck < 60) {
    var valueToCopy = sheet1.getRange("B8").getValue();
    sheet2.getRange("F2").setValue(valueToCopy);
    score = 2;
  }else if (valueToCheck < 50) {
    var valueToCopy = sheet1.getRange("B9").getValue();
    sheet2.getRange("F2").setValue(valueToCopy);
    score = 1;
  }

  return score;
}

function sleeptimescore() {
  var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
  var sheet1 = spreadsheet.getSheetByName("評価一覧");
  var sheet2 = spreadsheet.getSheetByName("評価シート");
  var score;

  sheet2.getRange("F3").clearContent();

  var valueToCheck = sheet2.getRange("D3").getValue();
  if (valueToCheck >= 8) {
    var valueToCopy = sheet1.getRange("B2").getValue();
    sheet2.getRange("F3").setValue(valueToCopy);
    score = 10;
  }else if (valueToCheck >= 7 && valueToCheck < 8) {
    var valueToCopy = sheet1.getRange("B3").getValue();
    sheet2.getRange("F3").setValue(valueToCopy);
    score = 8;
  }else if (valueToCheck >= 6 && valueToCheck < 7) {
    var valueToCopy = sheet1.getRange("B4").getValue();
    sheet2.getRange("F3").setValue(valueToCopy);
    score = 6;
  }else if (valueToCheck >= 5.5 && valueToCheck < 6) {
    var valueToCopy = sheet1.getRange("B5").getValue();
    sheet2.getRange("F3").setValue(valueToCopy);
    score = 5;
  }else if (valueToCheck >= 5 && valueToCheck < 5.5) {
    var valueToCopy = sheet1.getRange("B6").getValue();
    sheet2.getRange("F3").setValue(valueToCopy);
    score = 4;
  }else if (valueToCheck >= 4.5 && valueToCheck < 5) {
    var valueToCopy = sheet1.getRange("B7").getValue();
    sheet2.getRange("F3").setValue(valueToCopy);
    score = 3;
  }else if (valueToCheck >= 4 && valueToCheck < 4.5) {
    var valueToCopy = sheet1.getRange("B8").getValue();
    sheet2.getRange("F3").setValue(valueToCopy);
    score = 2;
  }else if (valueToCheck < 4) {
    var valueToCopy = sheet1.getRange("B9").getValue();
    sheet2.getRange("F3").setValue(valueToCopy);
    score = 1;
  }

  return score;
}

function stressscore() {
  var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
  var sheet1 = spreadsheet.getSheetByName("評価一覧");
  var sheet2 = spreadsheet.getSheetByName("評価シート");
  var score;

  sheet2.getRange("F4").clearContent();

  var valueToCheck = sheet2.getRange("D4").getValue();
  if (valueToCheck >= 50) {
    var valueToCopy = sheet1.getRange("B9").getValue();
    sheet2.getRange("F4").setValue(valueToCopy);
    score = 1;
  }else if (valueToCheck >= 45 && valueToCheck < 50) {
    var valueToCopy = sheet1.getRange("B8").getValue();
    sheet2.getRange("F4").setValue(valueToCopy);
    score = 2;
  }else if (valueToCheck >= 40 && valueToCheck < 45) {
    var valueToCopy = sheet1.getRange("B7").getValue();
    sheet2.getRange("F4").setValue(valueToCopy);
    score = 3;
  }else if (valueToCheck >= 35 && valueToCheck < 40) {
    var valueToCopy = sheet1.getRange("B6").getValue();
    sheet2.getRange("F4").setValue(valueToCopy);
    score = 4;
  }else if (valueToCheck >= 30 && valueToCheck < 35) {
    var valueToCopy = sheet1.getRange("B5").getValue();
    sheet2.getRange("F4").setValue(valueToCopy);
    score = 5;
  }else if (valueToCheck >= 20 && valueToCheck < 30) {
    var valueToCopy = sheet1.getRange("B4").getValue();
    sheet2.getRange("F4").setValue(valueToCopy);
    score = 6;
  }else if (valueToCheck >= 10 && valueToCheck < 20) {
    var valueToCopy = sheet1.getRange("B3").getValue();
    sheet2.getRange("F4").setValue(valueToCopy);
    score = 8;
  }else if (valueToCheck < 10) {
    var valueToCopy = sheet1.getRange("B2").getValue();
    sheet2.getRange("F4").setValue(valueToCopy);
    score = 10;
  }

  return score;
}

function bpmscore() {
  var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
  var sheet1 = spreadsheet.getSheetByName("評価一覧");
  var sheet2 = spreadsheet.getSheetByName("評価シート");
  var score;

  sheet2.getRange("F5").clearContent();

  var valueToCheck = sheet2.getRange("D5").getValue();
  if (valueToCheck >= 90) {
    var valueToCopy = sheet1.getRange("B9").getValue();
    sheet2.getRange("F5").setValue(valueToCopy);
    score = 1;
  }else if (valueToCheck >= 85 && valueToCheck < 90) {
    var valueToCopy = sheet1.getRange("B8").getValue();
    sheet2.getRange("F5").setValue(valueToCopy);
    score = 2;
  }else if (valueToCheck >= 80 && valueToCheck < 85) {
    var valueToCopy = sheet1.getRange("B7").getValue();
    sheet2.getRange("F5").setValue(valueToCopy);
    score = 3;
  }else if (valueToCheck >= 75 && valueToCheck < 80) {
    var valueToCopy = sheet1.getRange("B6").getValue();
    sheet2.getRange("F5").setValue(valueToCopy);
    score = 4;
  }else if (valueToCheck >= 70 && valueToCheck < 75) {
    var valueToCopy = sheet1.getRange("B5").getValue();
    sheet2.getRange("F5").setValue(valueToCopy);
    score = 5;
  }else if (valueToCheck >= 60 && valueToCheck < 70) {
    var valueToCopy = sheet1.getRange("B4").getValue();
    sheet2.getRange("F5").setValue(valueToCopy);
    score = 6;
  }else if (valueToCheck >= 50 && valueToCheck < 60) {
    var valueToCopy = sheet1.getRange("B3").getValue();
    sheet2.getRange("F5").setValue(valueToCopy);
    score = 8;
  }else if (valueToCheck < 50) {
    var valueToCopy = sheet1.getRange("B2").getValue();
    sheet2.getRange("F5").setValue(valueToCopy);
    score = 10;
  }

  return score;
}

function caloriesscore() {
  var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
  var sheet1 = spreadsheet.getSheetByName("評価一覧");
  var sheet2 = spreadsheet.getSheetByName("評価シート");
  var score;

  sheet2.getRange("F6").clearContent();

  var valueToCheck = sheet2.getRange("D6").getValue();
  if (valueToCheck >= 2000) {
    var valueToCopy = sheet1.getRange("B9").getValue();
    sheet2.getRange("F6").setValue(valueToCopy);
    score = 1;
  }else if (valueToCheck >= 1800 && valueToCheck < 2000) {
    var valueToCopy = sheet1.getRange("B8").getValue();
    sheet2.getRange("F6").setValue(valueToCopy);
    score = 2;
  }else if (valueToCheck >= 1500 && valueToCheck < 1800) {
    var valueToCopy = sheet1.getRange("B7").getValue();
    sheet2.getRange("F6").setValue(valueToCopy);
    score = 3;
  }else if (valueToCheck >= 1100 && valueToCheck < 1500) {
    var valueToCopy = sheet1.getRange("B6").getValue();
    sheet2.getRange("F6").setValue(valueToCopy);
    score = 4;
  }else if (valueToCheck >= 800 && valueToCheck < 1100) {
    var valueToCopy = sheet1.getRange("B5").getValue();
    sheet2.getRange("F6").setValue(valueToCopy);
    score = 5;
  }else if (valueToCheck >= 500 && valueToCheck < 800) {
    var valueToCopy = sheet1.getRange("B4").getValue();
    sheet2.getRange("F6").setValue(valueToCopy);
    score = 6;
  }else if (valueToCheck >= 300 && valueToCheck < 500) {
    var valueToCopy = sheet1.getRange("B3").getValue();
    sheet2.getRange("F6").setValue(valueToCopy);
    score = 8;
  }else if (valueToCheck < 300) {
    var valueToCopy = sheet1.getRange("B2").getValue();
    sheet2.getRange("F6").setValue(valueToCopy);
    score = 10;
  }

  return score;
}

function stepsscore() {
  var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
  var sheet1 = spreadsheet.getSheetByName("評価一覧");
  var sheet2 = spreadsheet.getSheetByName("評価シート");
  var score;

  sheet2.getRange("F7").clearContent();

  var valueToCheck = sheet2.getRange("D7").getValue();
  if (valueToCheck >= 4000) {
    var valueToCopy = sheet1.getRange("B9").getValue();
    sheet2.getRange("F7").setValue(valueToCopy);
    score = 1;
  }else if (valueToCheck >= 3200 && valueToCheck < 4000) {
    var valueToCopy = sheet1.getRange("B8").getValue();
    sheet2.getRange("F7").setValue(valueToCopy);
    score = 2;
  }else if (valueToCheck >= 2200 && valueToCheck < 3200) {
    var valueToCopy = sheet1.getRange("B7").getValue();
    sheet2.getRange("F7").setValue(valueToCopy);
    score = 3;
  }else if (valueToCheck >= 1500 && valueToCheck < 2200) {
    var valueToCopy = sheet1.getRange("B6").getValue();
    sheet2.getRange("F7").setValue(valueToCopy);
    score = 4;
  }else if (valueToCheck >= 1000 && valueToCheck < 1500) {
    var valueToCopy = sheet1.getRange("B5").getValue();
    sheet2.getRange("F7").setValue(valueToCopy);
    score = 5;
  }else if (valueToCheck >= 600 && valueToCheck < 1000) {
    var valueToCopy = sheet1.getRange("B4").getValue();
    sheet2.getRange("F7").setValue(valueToCopy);
    score = 6;
  }else if (valueToCheck >= 300 && valueToCheck < 600) {
    var valueToCopy = sheet1.getRange("B3").getValue();
    sheet2.getRange("F7").setValue(valueToCopy);
    score = 8;
  }else if (valueToCheck < 300) {
    var valueToCopy = sheet1.getRange("B2").getValue();
    sheet2.getRange("F7").setValue(valueToCopy);
    score = 10;
  }

  return score;
}

function sedentaryscore() {
  var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
  var sheet1 = spreadsheet.getSheetByName("評価一覧");
  var sheet2 = spreadsheet.getSheetByName("評価シート");
  var score;

  sheet2.getRange("F8").clearContent();

  var valueToCheck = sheet2.getRange("D8").getValue();
  if (valueToCheck >= 11) {
    var valueToCopy = sheet1.getRange("B9").getValue();
    sheet2.getRange("F8").setValue(valueToCopy);
    score = 1;
  }else if (valueToCheck >= 10 && valueToCheck < 11) {
    var valueToCopy = sheet1.getRange("B8").getValue();
    sheet2.getRange("F8").setValue(valueToCopy);
    score = 2;
  }else if (valueToCheck >= 8 && valueToCheck < 10) {
    var valueToCopy = sheet1.getRange("B7").getValue();
    sheet2.getRange("F8").setValue(valueToCopy);
    score = 3;
  }else if (valueToCheck >= 6 && valueToCheck < 8) {
    var valueToCopy = sheet1.getRange("B6").getValue();
    sheet2.getRange("F8").setValue(valueToCopy);
    score = 4;
  }else if (valueToCheck >= 5 && valueToCheck < 6) {
    var valueToCopy = sheet1.getRange("B5").getValue();
    sheet2.getRange("F8").setValue(valueToCopy);
    score = 5;
  }else if (valueToCheck >= 4 && valueToCheck < 5) {
    var valueToCopy = sheet1.getRange("B4").getValue();
    sheet2.getRange("F8").setValue(valueToCopy);
    score = 6;
  }else if (valueToCheck >= 3 && valueToCheck < 4) {
    var valueToCopy = sheet1.getRange("B3").getValue();
    sheet2.getRange("F8").setValue(valueToCopy);
    score = 8;
  }else if (valueToCheck < 3) {
    var valueToCopy = sheet1.getRange("B2").getValue();
    sheet2.getRange("F8").setValue(valueToCopy);
    score = 10;
  }

  return score;
}

function temperaturescore() {
  var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
  var sheet1 = spreadsheet.getSheetByName("評価一覧");
  var sheet2 = spreadsheet.getSheetByName("評価シート");
  var score;

  sheet2.getRange("F9").clearContent();

  var valueToCheck = sheet2.getRange("D9").getValue();
  if (valueToCheck >= 38) {
    var valueToCopy = sheet1.getRange("B9").getValue();
    sheet2.getRange("F9").setValue(valueToCopy);
    score = 1;
  }else if (valueToCheck >= 37.8 && valueToCheck < 38) {
    var valueToCopy = sheet1.getRange("B8").getValue();
    sheet2.getRange("F9").setValue(valueToCopy);
    score = 2;
  }else if (valueToCheck >= 37.7 && valueToCheck < 37.8) {
    var valueToCopy = sheet1.getRange("B7").getValue();
    sheet2.getRange("F9").setValue(valueToCopy);
    score = 3;
  }else if (valueToCheck >= 37.6 && valueToCheck < 37.7) {
    var valueToCopy = sheet1.getRange("B6").getValue();
    sheet2.getRange("F9").setValue(valueToCopy);
    score = 4;
  }else if (valueToCheck >= 37.4 && valueToCheck < 37.6) {
    var valueToCopy = sheet1.getRange("B5").getValue();
    sheet2.getRange("F9").setValue(valueToCopy);
    score = 5;
  }else if (valueToCheck >= 37.2 && valueToCheck < 37.4) {
    var valueToCopy = sheet1.getRange("B4").getValue();
    sheet2.getRange("F9").setValue(valueToCopy);
    score = 6;
  }else if (valueToCheck >= 37 && valueToCheck < 37.2) {
    var valueToCopy = sheet1.getRange("B3").getValue();
    sheet2.getRange("F9").setValue(valueToCopy);
    score = 8;
  }else if (valueToCheck < 37) {
    var valueToCopy = sheet1.getRange("B2").getValue();
    sheet2.getRange("F9").setValue(valueToCopy);
    score = 10;
  }

  return score;
}

function estimateStressLevel(bpm) {
  // 安静時の心拍数範囲
    const normalRateMin = 60;  // 通常の最小心拍数
    const maxHeartRate = 150;  // 最大心拍数(この値を超えるとストレスレベルは最大とみなす)

    if (bpm < normalRateMin) {
        return 0;  // 心拍数が60未満の場合はストレスレベル0
    } else {
        // 心拍数が60以上の場合、線形にストレスレベルを計算
        let stressLevel = (bpm - normalRateMin) / (maxHeartRate - normalRateMin) * 100;
        return Math.min(stressLevel, 100);  // ストレスレベルを0〜100の範囲に制限
    }
}

function doGet(e) {
  main();
  return ContentService.createTextOutput('更新完了。この画面を閉じてください。');
}

今回のスクリプトでは、main関数でOura APIのエンドポイントを指定します。
ConnectOuraAPI関数では、main関数で指定されたエンドポイントへアクセスし、データを取得してmain関数へデータを渡します。その後にmain関数では、受け取ったデータの配列の中から必要なデータののみを変数に格納します。
次にmain関数は、writeToSpreadsheet関数を呼び出し、main関数が受け取ったデータをスプレッドシートのD列の空欄部分に記載します。
その後、スプレッドシートのD列に記載されたデータの内容に沿った画像をF列に格納するため、「sleepscore()」「sleeptimescore()」「stressscore()」・・・の関数を呼び出します。

スプレッドシートIDの取得

先ほどのスクリプトの中で、下記のような「スプレッドシートのIDを記載」という箇所があったかと思います。スプレッドシートのIDとはどこなのかを簡単にご紹介します。

スプレッドシートIDはGoogleスプレッドシートのURLの中で /d//edit の間にある長い文字列がスプレッドシートのIDです。このIDは、英数字のランダムな組み合わせで構成されています。

他ツールからスクリプトを実行できるよう設定

次は以下の動画のように、作成したスクリプトをWebアプリケーションとして公開します。
「アクセスを承認」というボタンが出た場合、別でポップアップが出現するので、承認してください。そうすると、右下の「完了」ボタンが押せるようになります。

今回のアプリはOura APIから取得した自分自身のヘルス情報を表示するというものになりますが、僕の場合、一般向けに公開したりするわけではないので、このスクリプトを実行できるユーザー、アクセスできるユーザーを自分の身としておきます。
(※アクセスできるユーザーを制限したくない理由がなければ、セキュリティ保護の観点から、アクセスできるユーザーを自分の身としておいた方が良さそうです。)

ウェブアプリのURLをコピーして、どこかメモなどに貼り付けておいてください。
ここで取得したURLは、Glideで作成するアプリケーションのUIボタンが押された時のアクションとして使用できるように、次の記事で後程設定します。

(※URLの使われ方を簡単に説明すると、Webアプリケーションとして公開することで、上記のようにURLが取得できます。Glideで作成したボタンを操作することでこのURLへアクセスし、スクリプト内のdoGet関数で定義した処理(ここでは、main関数)を実行できるようにします。つまり、Glideで作成したUIボタンを押した時に、Oura APIにアクセスし、最新の情報を取得して、その情報をスプレッドシートに記載(≒Glideで作成したアプリの表示へ反映)できるようします。)

スクリプトの実行結果を確認

実際にスクリプトを実行すると、下記の画像のように、「体調シート」と「評価シート」のD列とF列の空欄だった場所に、Oura APIから取得したヘルスデータと、そのデータに対応した画像のURLが記載されていることがわかります。

以上で、Oura APIから取得したヘルスデータをGoogleスプレッドシートに記載できたことを確認できました。

まとめ

この記事では、Oura Ring APIを使用して健康データを取得し、それをGoogleスプレッドシートに記録する方法を紹介しました。ここまでで、パワプロ君っぽく自分自身のヘルス管理を行うためのアプリ開発が半分終わりました。次の記事では、このスプレッドシートのデータを使用してGlideでアプリケーションを作成する手順について説明します。

コメント