ハマログ

株式会社イーツー・インフォの社員ブログ

Backlog APIの活用方法を考え中

こんにちは、senです。

今年の秋ごろから、社内業務の改善方法を個人的に考えていましたが、普段よく利用するプロジェクト管理ツールであるBacklogで業務改善ができれば良いなと漠然と考えていました。

BacklogはAPIを提供しており、情報の取得や操作等をすることができます。

 

Backlog API – nulab

https://developer.nulab.com/ja/docs/backlog/#

 

一度使ってみようと思ってはいたので、Pythonでサンプルを作成してみました。

 

作成したサンプル

import requests
import json
from datetime import datetime
import pytz
from typing import Dict, Any, Optional
from secrets import BACKLOG_API_KEY, BACKLOG_SPACE_KEY

# タイムゾーンの設定
JST = pytz.timezone('Asia/Tokyo')

class BacklogAPI:
    """Backlog APIとの通信を管理するクラス"""
    
    def __init__(self, space_key: str, api_key: str):
        """
        Args:
            space_key: Backlogのスペースキー
            api_key: BacklogのAPIキー
        """
        self.base_url = f"https://{space_key}.backlog.jp/api/v2"
        self.params = {"apiKey": api_key}

    def get_issue(self, issue_key: str) -> Dict[str, Any]:
        """課題の詳細情報を取得

        Args:
            issue_key: 課題キー(例: "PROJECT-1")

        Returns:
            Dict[str, Any]: 課題の詳細情報
        """
        response = requests.get(
            f"{self.base_url}/issues/{issue_key}",
            params=self.params
        )
        response.raise_for_status()
        return response.json()

    def get_comments(self, issue_key: str) -> Dict[str, Any]:
        """課題のコメント一覧を取得

        Args:
            issue_key: 課題キー(例: "PROJECT-1")

        Returns:
            Dict[str, Any]: コメント一覧
        """
        response = requests.get(
            f"{self.base_url}/issues/{issue_key}/comments",
            params=self.params
        )
        response.raise_for_status()
        return response.json()

def format_datetime(dt_str: str) -> str:
    """ISO 8601形式の日時文字列を日本時間の読みやすい形式に変換

    Args:
        dt_str: ISO 8601形式の日時文字列(例: "2024-03-20T09:00:00Z")

    Returns:
        str: 日本語形式の日時文字列(例: "2024年03月20日 18:00")
    """
    # UTCの日時文字列をdatetimeオブジェクトに変換
    dt_utc = datetime.fromisoformat(dt_str.replace('Z', '+00:00'))
    # UTCからJSTに変換
    dt_jst = dt_utc.astimezone(JST)
    return dt_jst.strftime('%Y年%m月%d日 %H:%M')

def display_issue_info(issue: Dict[str, Any]) -> None:
    """課題の概要を表示

    Args:
        issue: 課題の詳細情報
    """
    print("=== 課題の概要 ===")
    print(f"課題キー: {issue['issueKey']}")
    print(f"タイトル: {issue['summary']}")
    print(f"状態: {issue['status']['name']}")
    print(f"優先度: {issue['priority']['name']}")
    print(f"担当者: {issue['assignee']['name'] if issue.get('assignee') else '未割り当て'}")
    print(f"作成者: {issue['createdUser']['name']}")
    print(f"作成日時: {format_datetime(issue['created'])}")
    print(f"更新日時: {format_datetime(issue['updated'])}")
    print(f"\n説明:\n{issue['description']}\n")

def display_attachments(issue: Dict[str, Any]) -> None:
    """添付ファイル情報を表示

    Args:
        issue: 課題の詳細情報
    """
    if issue.get('attachments'):
        print("=== 添付ファイル ===")
        for attachment in issue['attachments']:
            size_kb = attachment['size'] / 1024
            if size_kb < 1:
                print(f"- {attachment['name']} ({attachment['size']}バイト)")
            else:
                size_mb = size_kb / 1024
                if size_mb < 1:
                    print(f"- {attachment['name']} ({size_kb:.1f}KB)")
                else:
                    print(f"- {attachment['name']} ({size_mb:.1f}MB)")

def display_comments(comments: Dict[str, Any]) -> None:
    """コメント情報を表示

    Args:
        comments: コメント一覧を含む辞書
    """
    if comments:
        print("\n=== コメント一覧 ===")
        for i, comment in enumerate(comments, 1):
            print(f"\n{'─' * 50}")  # 区切り線
            print(f"▼ コメント {i}")
            print(f"投稿者: {comment['createdUser']['name']}")
            print(f"投稿日時: {format_datetime(comment['created'])}")

            # 更新履歴(チェンジログ)の表示
            if comment.get('changeLog'):
                print(f"更新内容:")
                print("    ┌" + "─" * 60)
                for change in comment['changeLog']:
                    field = change.get('field', '')
                    old_value = change.get('originalValue', '')
                    new_value = change.get('newValue', '')
                    print(f"    │ {field}: {old_value} → {new_value}")
                print("    └" + "─" * 60)

            # コメント内容の表示(Noneの場合は表示しない)
            if comment.get('content'):
                print(f"コメント内容:")
                content_lines = comment['content'].split('\n')
                print("    ┌" + "─" * 60)
                for line in content_lines:
                    print(f"    │ {line}")
                print("    └" + "─" * 60)
        print(f"\n{'─' * 50}")  # 最後の区切り線

def main():
    """メイン処理"""
    # 取得したい課題のキー
    issue_key = "ここにキーを入力"

    try:
        # Backlog APIクライアントの初期化
        api = BacklogAPI(BACKLOG_SPACE_KEY, BACKLOG_API_KEY)

        # 課題情報の取得と表示
        issue = api.get_issue(issue_key)
        display_issue_info(issue)
        display_attachments(issue)

        # コメント情報の取得と表示
        comments = api.get_comments(issue_key)
        display_comments(comments)

    except requests.exceptions.RequestException as e:
        print(f"APIリクエストエラーが発生しました: {e}")
        if hasattr(e, 'response'):
            print(f"ステータスコード: {e.response.status_code}")
            print(f"レスポンスボディ: {e.response.text}")
    except Exception as e:
        print(f"予期せぬエラーが発生しました: {e}")

if __name__ == "__main__":
    main()

 

実行結果は下記のようになります。

=== 課題概要 ===
課題キー: SAMPLE_ISSUE
タイトル: テスト課題
状態: 未対応
優先度: 中
担当者: e2info
作成者: e2info
作成日時: 2024年12月23日 18:36
更新日時: 2024年12月24日 20:08

説明:
BacklogAPIの活用をする課題です

=== コメント一覧 ===
──────────────────────────────────────────────────
▼ コメント 1
投稿者: e2info
投稿日時: 2024年12月24日 20:08
更新内容:
┌────────────────────────────────────────────
│ assigner: None → e2info
│ startDate: None → 2024-12-24
│ limitDate: None → 2024-12-27
│ resolution: None → 対応しない
└────────────────────────────────────────────

──────────────────────────────────────────────────
▼ コメント 2
投稿者: e2info
投稿日時: 2024年12月24日 09:26
コメント内容:
┌────────────────────────────────────────────
│ #テストコメント2
│
│ ###テストコメント3
└────────────────────────────────────────────

──────────────────────────────────────────────────
▼ コメント 3
投稿者: e2info
投稿日時: 2024年12月24日 09:25
コメント内容:
┌────────────────────────────────────────────
│ テストコメント1
└────────────────────────────────────────────

 

シンプルですが、課題の情報が取得できます。

こういったケース以外にも様々な自動化ができそうです。(時間がある時にドキュメントを読み直しておきます)

 

具体的な活用方法について

しかしながら、具体的にどう活用していくか?という点についてまだ考えがまとまっていません。

※社内ではGitHubとのユーザー名紐付けタスクや、課題の自動作成で事例がありました(下記記事参照)

バックログのユーザー名とGitHubのユーザー名を半自動で紐づけた話

Backlogの課題をPythonでまとめて作ってみました

 

それ以外に現状思いついたのは2パターンです。

 

1.AIを活用した課題分析

課題キーを入力して情報を送信し、分析結果を返してもらう案

 

2.n8nやDifyを活用したワークフローの構築

 

ワークフローの自動化ツールのn8nや

https://n8n.io/

ワークフローを含むAIアプリケーションをローコードで構築するDify等を活用して、プロジェクト管理の効率化や業務フローの一部を自動化する案。外部サービスとも連携。

https://dify.ai/

 

上記を考えました。

プロジェクト管理の効率化や業務フローの属人化の解消のための社内アプリを作成できれば一番良いですが、その場合APIの規約への考慮が増え、全社員がアクセスできるようにする等、開発の考慮点も増えていきます。

となると個人での活用方法を探していくことになりそうですが、現時点ではあまり良いアイデアもないため、社内で相談してみようかと考えています。

 

おわりに

年内最後の記事になりました。

2025年もBacklog APIの活用方法を模索していこうと思います。

backlogDifygithubn8n

  sen   2024年12月25日


関連記事

GoogleマップのAPIキーが設定されていなかったので設定しました

はじめに いまさらですが、Googleの以下の記事について。 Google Ma…

Amazon Linux 2にSupervisorを設定する

Laravelでキューワーカー(Queue Worker)を実行するために、Am…

MacOS XのGatekeeper

こんにちは、かねこです。 今流行のSASS/LESSをShin先輩に調べてもらっ…


← 前の投稿