概要
生活記録が欲しかったため、fitbitを使用したWebアプリケーションを開発した。
というより、fitbitを使うことが目的ではなく、ライフログアプリ(日常生活を記録するもの)を元に、睡眠だけ正確に計測する目的でfitbitを使用した。
また、LaravelでのPDF出力機能の実装が大変だったのでこの記事を書いた。
技術
領域 | 技術 |
---|---|
フロントエンド | Vue.js |
バックエンド | PHP(Laravel) |
ライブラリ | hammerjs 等など |
機能
カレンダー
・スワイプでの描画対象期間移動
・イベントの新規作成、更新、削除機能
・移動用の小さいカレンダー
スマホのWebブラウザで本アプリを使用する際、画面の大きさの都合上、前週・翌週に移動するためのボタンを設置するスペースがない。
そのため、スワイプ動作で前週・翌週に遷移する機能が欲しかった。
これはhammerjsを使って実装した。
スワイプに近い動作の実装をする要素が縦方向のスクロールにできる場合、cssでtouch-actionを設定しないと、スワイプ動作が検知されなかった。
要するに
const swipe = (e) => {
console.log('swipe');
if (e.direction === 4) {
//右方向
} else if(e.direction === 2) {
//左方向
}
};
といった処理の場合、
.timelines{
overflow-y: scroll;
overflow-x: hidden;
touch-action: pan-y; /* 必須*/
}
とする必要があるらしい。
fitbitアカウントでのログイン
fitbit APIを使って正確に睡眠時間を把握するためにこのWebアプリを開発に取り掛かった、という経緯が有ったためメールアドレスを使ったログインは実装していない。
fitbitAPIを利用した睡眠ログ取り込み
同一日の睡眠ログを重複して取り込む可能性があったため、ユーザと取り込み済み日付を格納するテーブルを用意し、同一睡眠ログを取り込めないように実装した。
habitの計測機能
画面上のボタンを押下することで、生活記録を計測を開始・終了・中止ができる。
睡眠時間帯をPDFに出力機能
これはChatGPTに適当に出力してもらったソースコードを調整して実装した。
あまりにも実装がめんどくさかったので、ソースコードを以下に記載。
public function getPDF(Request $request, Response $response)
{
//ftibti API実行クラス 各自用意する
$client = $this->getFitbitClient();
//開始期間と終了期間
$start_date = Carbon::parse("2024-3-01")->toDateString();
$end_date = Carbon::parse("2024-3-31")->toDateString();
//apiを叩く
$response = $client->fetchSleepLogByDateDateRange(
$start_date, $end_date
);
//jsonに変換
$sleep_log = json_decode($response->getBody(), true);
// すべての日付をリスト化(データがない日付も含める)
$allDates = [];
$period = new \DatePeriod(
new \DateTime($start_date),
new \DateInterval('P1D'),
(new \DateTime($end_date))->modify('+1 day')
);
foreach ($period as $date) {
$allDates[$date->format('Y-m-d')] = [];
}
// 睡眠データの処理
$sleepRecords = [];
foreach ($sleep_log['sleep'] as $sleep) {
$startTime = strtotime($sleep['startTime']);
$endTime = strtotime($sleep['endTime']);
$startDate = date('Y-m-d', $startTime);
$endDate = date('Y-m-d', $endTime);
$startIndex = (date('H', $startTime) * 6) + (int)(date('i', $startTime) / 10);
$endIndex = (date('H', $endTime) * 6) + (int)(date('i', $endTime) / 10);
if ($startDate != $endDate) {
// 前日分
if (isset($sleepRecords[$startDate])) {
$sleepRecords[$startDate][] = ['start' => $startIndex, 'end' => 144];
} else {
$sleepRecords[$startDate] = [['start' => $startIndex, 'end' => 144]];
}
// 翌日分
if (isset($sleepRecords[$endDate])) {
$sleepRecords[$endDate][] = ['start' => 0, 'end' => $endIndex];
} else {
$sleepRecords[$endDate] = [['start' => 0, 'end' => $endIndex]];
}
} else {
// 同じ日付内
if (isset($sleepRecords[$startDate])) {
$sleepRecords[$startDate][] = ['start' => $startIndex, 'end' => $endIndex];
} else {
$sleepRecords[$startDate] = [['start' => $startIndex, 'end' => $endIndex]];
}
}
}
// すべての日付を統合
foreach ($sleepRecords as $date => $records) {
$allDates[$date] = $records;
}
// PDF作成(横向き)
$pdf = new TCPDF('L', 'mm', 'A4');
$pdf->SetAutoPageBreak(true);
$pdf->AddPage();
$pdf->SetFont('Helvetica', '', 10);
// タイトル
$pdf->Cell(0, 10, "Fitbit Sleep Logs ({$start_date} - {$end_date})", 0, 1, 'C');
// 時間軸(10分単位)
$pdf->SetFillColor(200, 200, 200);
$pdf->Cell(12, 5, 'Date', 1, 0, 'C', true);
for ($hour = 0; $hour < 24; $hour++) {
$pdf->Cell(1.81 * 6, 5, $hour, 1, 0, 'C', true);
}
$pdf->Ln();
// 睡眠データを描画
foreach ($allDates as $date => $records) {
$pdf->Cell(12, 5, Carbon::parse($date)->format('m-d'), 1, 0, 'C');
for ($index = 0; $index < 144; $index++) {
$fillColor = [255, 255, 255]; // デフォルトは白
foreach ($records as $record) {
if ($index >= $record['start'] && $index < $record['end']) {
$fillColor = [0, 0, 255]; // 青色
break;
}
}
$pdf->SetFillColor($fillColor[0], $fillColor[1], $fillColor[2]);
$pdf->Cell(1.81, 5, '', 1, 0, 'C', true);
}
$pdf->Ln();
}
return response($pdf->Output('sleep_chart.pdf', 'S'))->header('Content-Type', 'application/pdf');
}
終わりに
デザインが壊滅的である。UIライブラリの導入をする必要があると感じた。