📊

Redmine API連携でプロジェクト進捗を見える化する実践ガイド

に公開

はじめに

Redmineはプロジェクト管理ツールとして広く利用されていますが、標準機能だけではプロジェクトの進捗状況を直感的に把握しづらいことがあります。特にバーンダウンチャートやカスタムレポートが必要な場合、プラグインを追加することになりますが、プラグインの追加はシステムパフォーマンスに影響を与える可能性があります。

本記事では、Redmineのプラグインに頼らず、REST APIを活用してプロジェクト進捗を可視化する方法を実践的に解説します。APIからチケット情報を取得し、週次でバーンダウンチャートを自動生成する仕組みを構築することで、軽量かつ柔軟な進捗管理が実現できます。

ソースコード

GitHubで公開しています。是非、ご自身のチームなどにあうように適時カスタマイズしてご活用ください。

Redmineプラグインの課題

Redmineには多数の便利なプラグインが存在しますが、プラグインに依存したシステム構築にはいくつかの問題があります。

まず、パフォーマンスの低下が挙げられます。プラグインを追加するたびにRedmineの起動時間が長くなり、画面表示が遅くなることがあります。特に複数のプラグインを組み合わせると、その影響は顕著になります。

次に、バージョン互換性の問題があります。Redmine本体をアップグレードする際に、プラグインが対応していないケースがあり、アップグレードが困難になることがあります。場合によっては、プラグインの更新を待つ必要があり、セキュリティパッチの適用が遅れるリスクもあります。

また、メンテナンスの負担も無視できません。プラグインごとに設定やアップデートが必要となり、管理工数が増加します。プラグイン開発者がメンテナンスを停止した場合、代替手段を検討する必要が生じます。

これらの課題を解決する方法として、Redmineが標準で提供するREST APIを活用する方法があります。APIを利用することで、Redmine本体に手を加えることなく、必要な機能を外部から実現できます。

Redmine REST APIの基本

RedmineのREST APIは、HTTP経由でチケット情報やプロジェクト情報にアクセスできる標準機能です。APIを使用することで、プログラムから簡単にデータを取得できます。

APIキーの取得

APIを使用するには、まずAPIキーを取得する必要があります。APIキーは各ユーザーごとに発行され、そのユーザーの権限でAPIを実行できます。

取得手順は次の通りです。まずRedmineにログインし、右上のアカウント名から「個人設定」を開きます。右側メニューの「APIアクセスキー」セクションで表示ボタンをクリックすると、APIキーが表示されます。

このAPIキーは機密情報ですので、環境変数や設定ファイルに保存する際は、Gitなどのバージョン管理システムに含めないよう注意が必要です。

APIエンドポイント

RedmineのAPIは、リソースごとに異なるエンドポイントを提供しています。主なエンドポイントを紹介します。

プロジェクト一覧の取得は、GET /projects.json で行います。このエンドポイントは、アクセス可能な全プロジェクトのリストを返します。

チケット一覧の取得は、GET /issues.json で行います。このエンドポイントでは、パラメータでプロジェクトやステータスを指定してフィルタリングできます。

特定のチケット情報の取得は、GET /issues/{id}.json で行います。チケットIDを指定することで、詳細な情報を取得できます。

カスタムフィールドを含む情報を取得する場合は、include=custom_fields パラメータを追加します。これにより、プロジェクト固有のカスタムフィールド情報も取得できます。

APIリクエストの実装

Pythonでのリクエスト実装例を示します。requests ライブラリを使用することで、シンプルにAPIにアクセスできます。

import requests

class RedmineClient:
    def __init__(self, base_url: str, api_key: str) -> None:
        self.base_url = base_url.rstrip("/")
        self.api_key = api_key
        self.headers = {
            "X-Redmine-API-Key": api_key,
            "Content-Type": "application/json"
        }

    def get_issues(
        self,
        project_id: str | None = None,
        status_id: str = "*",
        limit: int = 100
    ) -> dict:
        url = f"{self.base_url}/issues.json"
        
        params = {
            "status_id": status_id,
            "limit": limit,
            "include": "custom_fields"
        }
        
        if project_id:
            params["project_id"] = project_id
        
        response = requests.get(
            url,
            headers=self.headers,
            params=params,
            timeout=30
        )
        response.raise_for_status()
        return response.json()

このコードでは、ヘッダーにAPIキーを設定し、必要なパラメータを付与してリクエストを送信します。timeoutパラメータを設定することで、ネットワークの問題による長時間の待機を防げます。

ページネーション対応

RedmineのAPIは、1回のリクエストで取得できる件数に上限があります。デフォルトでは100件までとなっており、それ以上のチケットを取得する場合はページネーションが必要です。

ページネーションの実装では、offsetパラメータを使用して取得位置を指定します。total_countフィールドで総件数を確認し、全データを取得するまでリクエストを繰り返します。

def get_all_issues(
    self,
    project_id: str | None = None,
    max_issues: int = 1000
) -> list:
    all_issues = []
    limit = 100
    offset = 0
    
    while len(all_issues) < max_issues:
        data = self.get_issues(project_id, limit=limit, offset=offset)
        
        issues = data.get("issues", [])
        if not issues:
            break
        
        all_issues.extend(issues)
        
        total_count = data.get("total_count", 0)
        if len(all_issues) >= total_count:
            break
        
        offset += limit
    
    return all_issues[:max_issues]

このようにループ処理を行うことで、大量のチケット情報を効率的に取得できます。

プロジェクト進捗の可視化手法

取得したチケット情報を活用して、プロジェクトの進捗状況を可視化します。ここでは、バーンダウンチャートとステータス別推移グラフの作成方法を解説します。

バーンダウンチャートの考え方

バーンダウンチャートは、プロジェクトの残作業量を時系列で表示するグラフです。縦軸に残作業量、横軸に時間をとり、実際の作業進捗と理想的な進捗線を比較することで、プロジェクトの遅延や加速を視覚的に把握できます。

残作業量の計算には、チケット数ベースと工数ベースの2つの方法があります。チケット数ベースは、未完了のチケット数を残作業量とする方法で、シンプルで分かりやすいのが特徴です。工数ベースは、予定工数を基に残作業時間を計算する方法で、より正確な見積もりが可能ですが、各チケットに予定工数が入力されている必要があります。

理想線は、プロジェクト開始時の総作業量から、線形に減少した場合の値を計算します。実績線が理想線より上にある場合は遅延、下にある場合は予定より早く進捗していることを示します。

週次データの集計

チケットデータを週単位で集計することで、バーンダウンチャート用のデータを生成します。週番号はISO週番号を使用すると、国際標準に準拠した管理が可能です。

from datetime import datetime, timezone, timedelta

JST = timezone(timedelta(hours=9))

def get_week_number(date_str: str) -> str:
    if not date_str:
        return ""
    
    try:
        if " JST" in date_str:
            date_part = date_str.replace(" JST", "").strip()
            date = datetime.strptime(
                date_part,
                "%Y-%m-%d %H:%M:%S"
            ).replace(tzinfo=JST)
        elif "T" in date_str:
            date = datetime.fromisoformat(
                date_str.replace("Z", "+00:00")
            )
        else:
            date = datetime.strptime(
                date_str,
                "%Y-%m-%d"
            ).replace(tzinfo=UTC)
        
        iso_calendar = date.isocalendar()
        return f"{iso_calendar[0]}-W{iso_calendar[1]:02d}"
    except (ValueError, AttributeError):
        return ""

週番号を取得したら、週ごとにチケットを集計します。完了チケット数、残チケット数、完了工数、残工数を計算し、累積値として記録します。

理想線の計算では、プロジェクト全体の総作業量から、線形に減少させた値を算出します。進捗率を週数で割ることで、各週の理想的な残作業量を求めます。

CSV形式での出力

集計したデータは、CSV形式で出力することで、ExcelやGoogle Spreadsheetで簡単にグラフ化できます。CSV出力時は、BOM付き(!)UTF-8エンコーディングを使用することで、Excelでの文字化けを防げます。

出力するデータには、週番号、完了チケット数、残チケット数、完了工数、残工数、理想残チケット数、理想残工数を含めます。これにより、Excelでデータ範囲を選択するだけで、簡単にグラフを作成できます。

グラフの作成

CSVファイルをExcelまたはGoogle Spreadsheetで開き、折れ線グラフを作成します。X軸に週番号、Y軸に残チケット数をとり、実績線と理想線の2本の線を表示します。

実績線は青色の太線、理想線は灰色の点線にすることで、視覚的に区別しやすくなります。実績線が理想線より上にある週は遅延していることを示し、対策が必要であることが一目で分かります。

カスタムフィールドへの対応

Redmineでは、プロジェクトごとにカスタムフィールドを定義できます。APIでカスタムフィールドを取得するには、include=custom_fields パラメータを指定する必要があります。

カスタムフィールドは、各チケットの custom_fields 配列に格納されています。フィールド名で検索して値を取得し、CSV出力時に列として追加することで、カスタムフィールド情報も含めたレポートが作成できます。

def _extract_custom_fields(self, issues: list) -> set:
    custom_field_names = set()
    for issue in issues:
        custom_fields = issue.get("custom_fields", [])
        if isinstance(custom_fields, list):
            for field in custom_fields:
                if isinstance(field, dict) and "name" in field:
                    custom_field_names.add(field["name"])
    return custom_field_names

def _get_custom_field_value(self, issue: dict, field_name: str) -> str:
    custom_fields = issue.get("custom_fields", [])
    if isinstance(custom_fields, list):
        for field in custom_fields:
            if isinstance(field, dict) and field.get("name") == field_name:
                value = field.get("value", "")
                if isinstance(value, list):
                    return ", ".join(str(v) for v in value)
                return str(value) if value is not None else ""
    return ""

全チケットからカスタムフィールド名を抽出し、CSV出力時にヘッダー行に追加します。これにより、プロジェクト固有の情報も含めた包括的なデータ管理が可能になります。

運用フローの自動化

プロジェクト管理の効率化には、データ取得から可視化までの一連の作業を自動化することが重要です。ここでは、週次で自動実行する仕組みを構築します。

スクリプト化

データ取得からCSV生成までの処理をスクリプトにまとめることで、コマンド一つで実行できるようにします。環境変数で接続情報を管理することで、セキュリティを保ちながら柔軟な設定が可能です。

#!/usr/bin/env bash
set -euo pipefail

echo "チケットデータ取得開始"
python -m redmine_burndown.redmine_client

echo "バーンダウンデータ生成開始"
python -m redmine_burndown.burndown_analyzer

echo "処理完了"

環境変数は.envファイルに記述し、Gitの管理対象外とすることで、APIキーなどの機密情報を安全に管理できます。

定期実行の設定

週次での自動実行には、cronジョブを使用します。毎週月曜日の朝9時に実行する設定例を示します。

0 9 * * 1 cd /path/to/redmine-burndown && ./update_burndown.sh

この設定により、毎週自動的に最新のプロジェクト進捗データが更新されます。チーム会議の前に実行することで、常に最新の状況を把握した上で議論できます。

エラーハンドリング

自動実行では、エラーが発生した場合の対応が重要です。APIの接続エラー、認証エラー、データ形式エラーなど、想定されるエラーに対して適切な処理を行います。

ログファイルに詳細なエラー情報を記録することで、問題の原因を特定しやすくなります。ログレベルを適切に設定し、INFO、WARNING、ERRORの情報を分けて記録します。

import logging

logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
    datefmt="%Y-%m-%d %H:%M:%S"
)

logger = logging.getLogger(__name__)

try:
    response = requests.get(url, headers=headers, timeout=30)
    response.raise_for_status()
except requests.exceptions.RequestException as e:
    logger.error("チケット取得エラー: %s", e)

エラー通知を設定することで、問題発生時に素早く対応できます。メール通知やSlack通知を組み込むことで、チーム全体で状況を共有できます。

パフォーマンスとセキュリティの考慮

API連携では、パフォーマンスとセキュリティの両面に配慮する必要があります。

リクエスト頻度の制御

APIリクエストの頻度が高すぎると、Redmineサーバーに負荷をかける可能性があります。必要最小限のリクエスト回数に抑えることが重要です。

バッチ処理として実行する場合は、取得したデータをローカルにキャッシュし、再度取得する必要がないようにします。リアルタイムでの取得が不要な場合は、週次や日次での更新で十分です。

リトライ処理を実装する際は、指数バックオフを使用して、サーバーへの負荷を軽減します。最大リトライ回数を設定し、無限ループを避けます。

APIキーの管理

APIキーは、そのユーザーの全権限でAPIを実行できる重要な情報です。適切な管理が必要です。

APIキーは環境変数や設定ファイルに保存し、ソースコードに直接記述しません。.envファイルを使用する場合は、.gitignoreに追加してGit管理から除外します。

本番環境では、専用のAPIキーを発行し、開発環境とは分けて管理します。不要になったAPIキーは速やかに無効化します。

権限の最小化も重要です。API実行に必要な最低限の権限を持つユーザーでAPIキーを発行することで、万が一キーが漏洩した場合の被害を最小限に抑えられます。

タイムアウト設定

ネットワークの問題でリクエストが長時間待機状態になることを防ぐため、適切なタイムアウト値を設定します。通常のAPIリクエストでは30秒程度が適切です。

response = requests.get(
    url,
    headers=headers,
    params=params,
    timeout=30,
    verify=True
)

verify=Trueを設定することで、SSL証明書の検証を行い、中間者攻撃のリスクを軽減できます。

プロジェクト進捗分析のポイント

バーンダウンチャートから得られる情報を正しく解釈し、適切な対応を取ることが重要です。

遅延の兆候を早期発見

実績線が理想線より上にある状態が続く場合、プロジェクトが遅延していることを示します。早い段階でこの兆候を発見することで、対策を講じる時間を確保できます。

遅延の原因を特定するために、ステータス別のチケット分布を確認します。特定のステータスでチケットが滞留している場合、そこがボトルネックになっている可能性があります。

担当者別の進捗状況を確認することも有効です。特定の担当者にチケットが集中している場合、負荷分散が必要かもしれません。

スコープ変更の把握

実績線が急激に上昇した場合、新規チケットが大量に追加されたことを示します。スコープ変更が発生した可能性があり、計画の見直しが必要です。

週次のチケット増加数を記録することで、スコープ変更の頻度と規模を把握できます。頻繁にスコープが変更される場合は、要件定義や優先順位付けのプロセスに問題がある可能性があります。

停滞の検出

実績線が横ばいになっている場合、作業が停滞していることを示します。ブロッカーとなっているチケットがないか確認が必要です。

長期間ステータスが変わらないチケットを抽出し、個別に状況を確認します。技術的な問題で詰まっている場合は、サポートが必要かもしれません。

まとめ

RedmineのREST APIを活用することで、プラグインに依存せずにプロジェクト進捗を可視化できます。APIから取得したデータを週次で集計し、バーンダウンチャートを作成することで、プロジェクトの状況を直感的に把握できます。

この方法には、いくつかの利点があります。まず、Redmine本体への影響がないため、パフォーマンスの低下を心配する必要がありません。次に、外部ツールとして実装するため、Redmineのバージョンアップに影響されにくく、長期的にメンテナンスしやすくなります。

また、データをCSV形式で出力することで、Excelやスプレッドシートなど、チームが慣れ親しんだツールで可視化できます。必要に応じてグラフの種類や表示方法をカスタマイズできる柔軟性も魅力です。

週次での自動実行を設定することで、常に最新のプロジェクト状況を把握できます。定期的な振り返りの場で活用することで、プロジェクトの健全性を維持し、問題の早期発見と対処が可能になります。

Redmine APIを活用したプロジェクト管理の可視化は、シンプルでありながら強力な手法です。ぜひ実践して、チームのプロジェクト管理を改善してください。

参考リンク

Discussion