togglで勤怠管理、Redmineのチケットに作業時間を付ける運用時、両者の合計は一致するはずだが、手入力運用では面倒。
togglデータにRedmineのチケット情報を持たせて、Redmineへ時間データを付けられたら必ず一致して手間が省けて超便利です。
ということで各APIを呼び出し、処理するツールを作成しました。
フロー
前提
togglのdescriptionに、所定の形式でRedmineのチケット番号を書く
(例)黄色部分。チケット番号 12533
コード
※細かいエラー処理は省いています。
※実際のコードから抜粋しているため、修正ミスがある(このままでは動かない)かもしれません。あくまでざっとした流れの参考として見てください。
toggl APIを利用してデータ取得するメソッド
ToggleRepository.php
/** * Toggglの作業記録を取得する * * @param string $apiToken * @param string $startDate='' * @param string $endDate='' * @return mixed * */ public function getEntries( string $apiToken, string $startDate='', string $endDate='' ): mixed { $togglUri = 'https://api.track.toggl.com/api/v9/me/time_entries'; // 実際はconfig等に設定しておく $options = ['auth' => [$apiToken, 'api_token']]; // 取得対象のユーザのapi tokenをセットする $query = http_build_query([ 'start_date' => $startDate, 'end_date' => $endDate, ]); $entryRequest = (new \GuzzleHttp\Client())->request('GET', "{$togglUri}?{$query}", $options); $res = $entryRequest->getBody()->getContents(); return json_decode($res, true); }
Redmine APIを利用してデータ登録するメソッド
/** * Redmineに時間エントリを登録する * * @param array $xmlValues * @return bool * */ public function addTimeEntry(string $apiToken, array $xmlValues) : bool { $xml = new \SimpleXMLElement('<time_entry></time_entry>'); foreach ($xmlValues as $key => $value) { $xml->addChild($key, (string)$value); } //Redmine に作業時間を登録する $redmineUri = self::REDMINE_URL . "time_entries.xml?key={$apiToken}"; $request = (new \GuzzleHttp\Client())->request('POST', $redmineUri, [ 'headers' => ['Content-Type' => 'text/xml',], 'body' => $xml->asXML() ]); return true; }
メイン処理
/** * main処理 * @return array */ private function exec() :array { $this->togglApiToken = 'xxxxxxxxxxxxx' // 取得対象のユーザのapi token $this->redmineApiKey = 'ZZZZZZZZZZZZZ'; // 対象ユーザのRedmineのAPIキー $this->redmineUserId = 0; // 対象ユーザのRedmineのユーザID // toggl : 作業記録を取得する $entries = $this->toggleRepository->getEntries( $this->togglApiToken, $this->dateTime->setTime(00, 00, 00)->format('c'), $this->dateTimeEnd->setTime(23, 59, 59)->format('c') ); // toggl の作業内容から Redmine用の作業時間データを作成し登録する return $this->makeRecord($entries); }
/** * Toggl の作業内容から Redmine用の作業時間データを作成し登録する * * @param array $entries * @param array $redmineTimeRecs * @return array : 実行結果 * */ private function makeRecord(array $entries, array $redmineTimeRecs) : array { foreach ($entries as $entry) { $description = ''; // 作業時間の時間 // 開始・終了の差分 $start = new \DateTime($entry['start']); $start->setTimezone($this->timezone); if (!isset($entry['stop'])) { $this->results->addIssueSkipNowExec($start, $description, 'timer 実行中です'); continue; } $stop = new \DateTime($entry['stop']); $stop->setTimezone($this->timezone); $diff = $stop->diff($start); $min = round(($diff->i / 60), 3); $diffHours = $diff->h + $min; // Redmine でタイマーを打ったときのフォーマットに沿って` #1234 `の文字列をチケットIDとして検索 preg_match('/\#([0-9]+)/', $description, $preg_match_results); //Redmine に作業時間を登録する $ret = $this->addToRedmine($issueId, $entry, $start, $diffHours, $description); if ($ret) { // 成功時処理 } else { // エラー時処理 } } return [ // 結果 ]; }
/** * Redmine に作業時間を登録する * * @param int $issueId * @param array $entry * @param Datetime $start * @param float $diffHours * @param string $description * @return bool * */ private function addToRedmine ( int $issueId, array $entry, Datetime $start, float $diffHours, string $description ) : bool { $xmlValues = [ 'issue_id' => $issueId, 'spent_on' => $start->format('Y-m-d'), 'hours' => $diffHours, 'activity_id' => 8, // $entry['tags']からタグが取得できるので、必要ならRedmineのタグのIDと対応させてセットする 'user_id' => $this->redmineUserId, // 対象ユーザのRedmineのユーザID 'comments' => $description, ]; return $this->redmineRepository->addTimeEntry($this->redmineApiKey, $xmlValues); }
実行結果
Redmine、赤部分が追加されている。
Note
実際には、以下の処理に対応しています
・すでにredmineに時間登録済みか?重複チェック
・redmineに該当番号のチケットがあるか?チェック
・該当番号のチケットにそのユーザが権限を持っているか?チェック