Redmineのチケット、期限切れチケットの一覧を
DataFrameで還元するコード。
テストコードは引き続き書き中。
dataframe→Mardownテーブル化→Chat投入の流れで使用します。
Markdownテーブル化、チャット投入ライブラリは
既に作ってあります。
#!/opt/anaconda3/bin/python3
# -*- coding: utf-8 -*-
"""Redmineチケット状態を管理する
チケット情報を得て状況分析を行う
Todo:
* まだRedmineとRocketChatのみ。他のOSSに対しても同様に作る
"""
################################################
# library
################################################
import datetime
import redminelib
import requests
import pandas as pd
import sys
from pprint import pprint
from redminelib import Redmine
# 個別ライブラリLoad
################################################
# 独自例外定義
################################################
#class MaxRetryError(Exception):
# pass
#
################################################
# 情報定義
################################################
#
## Redmine Instance
#HOST = "http://xxxxxxxxxxxxxxxxxxxxxxxxxx:3100/"
#API_KEY = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx47ac"
#
## 抽出対象
#PROJECT_ID = 'wbs-kaisui'
#PROJECT_NAME = '個別WBS(開発推進)'
#TRACKER_NAME ='やること'
#
## Dataframe columns定義
#COLUMNS = ['Project','Tracker','Sprint','親チケットID','チケットID','TicketTitle','担当', 'Status','開始日','期限']
#
## 探索条件
#QUERY = '開始日 <= @TODAY and 期限 < @TODAY'
#
## Sort条件
#SORT_KEY = ['担当','期限']
#
################################################
# RedmineTicketManager
################################################
class RedmineTicketManager():
"""Redmineチケット管理Class
Attributes:
API_KEY (int) : Redmineのadmin API key
HOST (bool) : Redmineのホスト
"""
def __init__(self, HOST, API_KEY):
"""Redmineインスタンス生成
API_KEY,HOSTからRedmineインスタンスを返す
Args:
HOST (str): Redmine Host
API_KEY (str): adminに応じたAPI_KEY
Returns:
Raises:
TypeError: 引数型の不備
Exception: Redmineインスタンス生成不備
Examples:
>>> redmine = RedmineUserManager(HOST, API_KEY)
Note:
Redmineのグループは事前に存在する必要があります。
新しいグループが記載されたとしても自動でRedmine上で作成しません。
__init__ではboolを返してはならないので留意
"""
# 引数チェック 型
if not isinstance(HOST, str):
print(f'引数:HOSTの型が正しくありません str <-> {type(HOST)}')
raise TypeError
if not isinstance(API_KEY, str):
print(f'引数:API_KEYの型が正しくありません str <-> {type(API_KEY)}')
raise TypeError
#TODO 引数チェク追加
# Redmineインスタンス生成(self.redmine)
try:
self.redmine = Redmine(HOST, key=API_KEY)
except exception as e:
print(f'Redmine Instance生成に失敗しました')
print(f'エラー詳細:{e}')
else:
if not isinstance(self.redmine, redminelib.Redmine):
print(f'Redmine Instance生成に失敗しました')
raise Exception
########################################################
# 期限切れチケット探索処理
########################################################
def createOverdueTicket(self, PROJECT_ID, TRACKER_NAME):
'''期限切れチケットを探索する
プロジェクト、トラッカーを指定して期限超過チケットを探索する。
Args:
PROJECT_ID : str RedmineプロジェクトID(プロジェクト名ではない)
TRACKER_NAME: str Redmine検索対象とするトラッカー名
Returns:
プロジェクト、トラッカー上限にヒットするチケット全量: DataFrame
プロジェクト、トラッカー条件にヒットし、かつ期限超過チケット: DataFrame
Raises:
TypeError: 引数型の不備
Exception: チケット探索時の例外
Examples:
>>> RTM = RedmineTicketManager(HOST, API_KEY)
>>> df, df_overdue = RTM.createOverdueTicket(PROJECT_ID, TRACKER_NAME)
Note:
'''
# Dataframe columns定義
COLUMNS = ['Project','Tracker','Sprint','親チケットID','チケットID','TicketTitle','担当', 'Status','開始日','期限']
# 探索条件
QUERY = '開始日 <= @TODAY and 期限 < @TODAY'
# Sort条件
SORT_KEY = ['担当','期限']
# 今日日付生成
TODAY = datetime.date.today()
pprint(f'判定基準日: {TODAY}')
INIT_DATE = datetime.date(1900, 1, 1)
pprint(f'始まりの日: {INIT_DATE}')
print('-'*80)
print(f'Redmineチケットを探索します')
try:
# 管理者権限で全てのチケットを取得
tickets=self.redmine.issue.filter(
project_id=PROJECT_ID,
tracker_name=TRACKER_NAME,
)
except Exception as e:
print(f'チケット取得に失敗しました:{PROJECT_ID} {TRACKER_NAME}')
print(f'エラー詳細:{e}')
print()
else:
# チケット遅延判定処理、データ作成
# 仮の入れ物を用意
_list = []
# チケット成形処理
for ticket in tickets:
# ticketインスタンスから情報取得
projectName = ticket.project.name
id = ticket.id
subject = ticket.subject
authorName = ticket.author.name
authorID = ticket.author.id
status = ticket.status.name
tracker = ticket.tracker.name
fixed_version = ticket.fixed_version.name
# 担当者が設定されていない場合は未設定と表示
try:
assigned_to = ticket.assigned_to.name
except:
assigned_to = "未設定"
# 開始日が設定されていない場合は始まりの日を設定
try:
startDate = ticket.start_date
except:
startDate = INIT_DATE
# 期日が設定されていない場合は始まりの日を設定
try:
dueDate = ticket.due_date
except:
dueDate = INIT_DATE
# 親チケットが設定されていいない場合は0を設定
try:
parent = ticket.parent
except:
parent = 0
# 蓄積
_list.append([projectName, tracker, fixed_version, parent, id, subject, assigned_to, status, startDate, dueDate])
print(f'チケット抽出が完了しました')
# DataFrame化
df = pd.DataFrame(_list)
df.columns = COLUMNS
# 遅延チケット抽出
# 開始日が今日以前、期限が本日超過しているものを対象とする
df_overdue = df.query(QUERY).sort_values(SORT_KEY).reset_index(drop=True)
print(f'期限超過Redmineチケットを抽出しました')
print(f'期限超過チケット件数: {len(df_overdue)}')
return df ,df_overdue
########################################################
# 期限切れチケット探索処理 by REST
########################################################
def createOverdueTicketByREST(self, RESTAPI, HEADERS, PROJECT_ID, TRACKER_ID):
'''期限切れチケットを探索する By REST
プロジェクト、トラッカーを指定して期限超過チケットを探索する。
python_redmineを使用せず直接RESTを使って情報取得する。
Args:
PROJECT_ID : str RedmineプロジェクトID(プロジェクト名ではない)
TRACKER_ID: str Redmine検索対象とするトラッカーID(トラッカー名ではない)
Returns:
プロジェクト、トラッカー上限にヒットするチケット全量: DataFrame
プロジェクト、トラッカー条件にヒットし、かつ期限超過チケット: DataFrame
Raises:
TypeError: 引数型の不備
Exception: チケット探索時の例外
Examples:
>>> RTM = RedmineTicketManagerByREST(HOST, API_KEY)
>>> df, df_overdue = RTM.createOverdueTicketByREST(PROJECT_ID, TRACKER_ID)
Note:
NAMEなのかIDなのか、使い分けがめんどい感じがある。。。
'''
# request parameter生成
#URL = f'http://192.168.10.104:3100'
#API = f'{URL}/projects/{PROJECT_ID}/issues.json?tracker_id={TRACKER_ID}'
#HEADERS = { 'Content-Type': 'application/json', 'X-Redmine-API-Key': '864b3f0933e8084295d47380bf07a168ba2947ac'}
# 今日日付生成
TODAY = datetime.date.today()
pprint(f'判定基準日: {TODAY}')
# Dataframe columns定義
COLUMNS = ['Project','Tracker','Sprint','親チケットID','チケットID','TicketTitle','担当', 'Status','開始日','期限']
# 探索条件
QUERY = '開始日 <= @TODAY and 期限 < @TODAY'
# Sort条件
SORT_KEY = ['担当','期限']
# 探索処理実行
## 入れ物を用意
_list = []
# 探索実施
try:
response = requests.get(RESTAPI,
headers=HEADERS)
except Exception as e:
print(f'Redmineチケット探索に失敗しました')
print(f'{e}')
else:
# 結果加工
for _ in response.json()['issues']:
# 親と担当者はデータとして存在しないケースあり
# try〜catchで判定する必要がある
try:
parent = _['parent']['id']
except:
parent = 0
try:
assigned_to = _['assigned_to']['name']
except:
assigned_to = "None"
# 開始、期限は存在する。
# 設定がない場合は Noneが入っている
if _['start_date'] != None:
startDate = _['start_date']
else:
startDate = '1900-01-01'
if _['due_date'] != None:
dueDate = _['due_date']
else:
dueDate = '1900-01-01'
# データ蓄積
_list.append([_['project']['name'],
_['tracker']['name'],
_['fixed_version']['name'],
parent,
_['id'],
_['description'],
assigned_to,
_['status']['name'],
startDate,
dueDate,
])
# DataFrameに変換して抽出処理実施
df = pd.DataFrame(_list)
df.columns = COLUMNS
df['開始日'] = pd.to_datetime(df['開始日'])
df['期限'] = pd.to_datetime(df['期限'])
# 遅延チケット抽出
# 開始日が今日以前、期限が本日超過しているものを対象とする
df_overdue = df.query(QUERY).sort_values(SORT_KEY).reset_index(drop=True)
print(f'期限超過Redmineチケットを抽出しました')
print(f'期限超過チケット件数: {len(df_overdue)}')
return df ,df_overdue