複数プロジェクトの一覧情報をタイムリーに確認する
概要
TimeTracker NXはプロジェクトの横断管理にも対応していますが、Web APIを活用することで、複数プロジェクトの情報をさらに柔軟に必要な情報を取得することができます。 全社単位あるいは部門単位など、必要なレベルで必要なデータをタイムリーに管理できます。
利用場面
- 各プロジェクトの工数やコストの状況を全社単位で確認する
- 部門内で稼働中の各プロジェクトについて、進捗状況を比較する
使用するAPI
以下のAPIを使用します。
- プロジェクトの取得
- ワー クアイテムの取得
サンプルスクリプト
- JavaScript
- C#
//======================================================================
// プロジェクト情報を出力する
//======================================================================
//======================================================================
// 外部ファイル(ライブラリ、他ファイル)のインクルード
//======================================================================
const https = require('https'); // HTTPS通信を利用する場合に設定する
const http = require('http'); // HTTP通信を利用する場合に設定する
const axios = require('axios');
const fs = require('fs');
const iconv = require("iconv-lite");
//======================================================================
// 共通部の定義
//======================================================================
// APIを呼び出すURLを設定する
const baseURLString = 'http://yourserver/TimeTrackerNX/api';
// サーバースペックに合わせてウェイト時間を設定する
// 1分間に1500件程度の処理に収まるようするため、
// 目安として推奨スペックでは100ms程度とする
const requestWaitTime = 100; //単位:ms
//----------------------------------------------------------------------
// Web API間の処理待ち関数
//----------------------------------------------------------------------
async function wait(time){
const d1 = new Date();
while (true) {
const d2 = new Date();
if (d2 - d1 > time) {
break;
}
}
}
//======================================================================
// メイン処理
//======================================================================
main();
async function main(){
// 引数チェック
if (process.argv.length < 3){
return 'enter username and password';
}
const userName = process.argv[2];
var pass;
var authString; //ブロック内スコープから外に出すためvarで宣言(固定値)
// 引数でパスワードが未入力の場合は空白とみなす
if (process.argv.length > 3){
pass = process.argv[3];
} else {
pass = '';
}
// 認証情報を取得する
var sendUrl = baseURLString + '/auth/token';
try{
const { data } = await axios.post(
sendUrl,
{
loginName : userName,
password : pass
},
/*
// https通信時に使用する
httpsAgent : new https.Agent({
rejectUnauthorized: false
})
*/
);
authString = 'Bearer ' + data.token;
} catch (err) {
console.log(err);
}
// 以降のAPI通信で利用する送信ヘッダを作成する
const request = axios.create({
baseURL : baseURLString,
headers :{
'Authorization' : authString
},
/*
// https通信時に使用する
httpsAgent : new https.Agent({
rejectUnauthorized: false
})
*/
});
// プロジェクト情報を取得する
var sendUrl = '/project/projects';
let rootWorkItemIds = "";
// 稼働中のプロ ジェクト情報を取得する(リクエストに稼働状態の条件を設定して送信)
try {
const { data } = await request.get(
sendUrl,
{
params: {
isFinished :"false"
}
}
);
// 初回のデータ書き込み時に見出しを追加するためのフラグを設定
var headerFlag = 1;
// 取得したプロジェクトからデータを取得する
for (var key in data.data){
sendUrl = '/workItem/workItems/' + data.data[key].workItemRootFolderId + '/subItems';
// ルートワークアイテムのフィールド情報の取得 (リクエストに出力するフィールドを設定して送信)
try {
const { data } = await request.get(
sendUrl,
{
params: {
fields : 'ProjectId,ProjectName,ProjectCode,StatusTypeId,Description,ItemNumber,ActualProgress,PlannedStartDate,PlannedFinishDate,PlannedTime,ActualTime,ActualStartDate,ActualFinishDate,RemainingTime,PlannedCost,ActualCost,OutputScalePlan,OutputScaleActual,OutputUnit,OutputInfo,LeaderId,FinishDate,WorkingDayCount,PassedWorkingDayCount,SPI,TPI,CPI',
depth : 0
}
}
);
// 取得したデータをCSV形式で出力する
outputData(headerFlag, data);
// 初回のデータ書き込みが終わったタイミングでフラグを無効にする
if(headerFlag == 1){
headerFlag = 0;
}
} catch (e) {
console.log(e);
}
await wait(requestWaitTime);
}
} catch (e) {
console.log(e);
}
}
function outputData(headerFlag, data){
var filepath = './output.csv';
var buffer;
// 初回の書き込みのみヘッダを追加
if(headerFlag == 1){
buffer = 'Id,ProjectId,ProjectName,ProjectCode,StatusTypeId,Description,ItemNumber,ActualProgress,PlannedStartDate,PlannedFinishDate,PlannedTime,ActualTime,ActualStartDate,ActualFinishDate,RemainingTime,PlannedCost,ActualCost,OutputScalePlan,OutputScaleActual,OutputUnit,OutputInfo,LeaderId,FinishDate,WorkingDayCount,SPI,TPI,CPI,IsAclInherited,CanEdit\n';
fs.writeFileSync(filepath, iconv.encode(buffer, 'Shift_JIS'), function(err){
if (err) {
console.log(err);
}
});
}
// 1行分ごとにデータをファイルへ追記する
for (var key in data){
var array = [
data[key].fields.Id,
data[key].fields.ProjectId,
data[key].fields.ProjectName,
data[key].fields.ProjectCode,
data[key].fields.StatusTypeId,
data[key].fields.Description,
data[key].fields.ItemNumber,
data[key].fields.ActualProgress,
data[key].fields.PlannedStartDate,
data[key].fields.PlannedFinishDate,
data[key].fields.PlannedTime,
data[key].fields.ActualTime,
data[key].fields.ActualStartDate,
data[key].fields.ActualFinishDate,
data[key].fields.RemainingTime,
data[key].fields.PlannedCost,
data[key].fields.ActualCost,
data[key].fields.OutputScalePlan,
data[key].fields.OutputScaleActual,
data[key].fields.OutputUnit,
data[key].fields.OutputInfo,
data[key].fields.LeaderId,
data[key].fields.FinishDate,
data[key].fields.WorkingDayCount,
data[key].fields.SPI,
data[key].fields.TPI,
data[key].fields.CPI,
data[key].fields.IsAclInherited,
data[key].fields.CanEdit
]
buffer = array.join(',') + '\n';
fs.appendFileSync(filepath, iconv.encode(buffer, 'Shift_JIS'), function(err){
if (err) {
console.log(err);
}
});
}
}
private async void ExportProject_Click(object sender, EventArgs e)
{
//======================================================//
// 必要な変数等の定義 //
//======================================================//
JavaScriptSerializer serializar = new JavaScriptSerializer();
var httpClient = new HttpClient();
string serverUrl = "http://WIN-Q1OR68LM6RG.mshome.net/TimeTrackerNX/";
string loginname = "okamoto";
string password = "";
// トークンの取得
var tokenRequest = new Dictionary<string, string>
{
{"loginName",loginname },
{"password",password }
};
var jsonData = serializar.Serialize(tokenRequest);
var content = new StringContent(jsonData, Encoding.UTF8, "application/json");
string requestUrl = serverUrl + "/api/auth/token";
var response = await httpClient.PostAsync(requestUrl, content);
string responseData = await response.Content.ReadAsStringAsync();
Token token;
try
{
token = serializar.Deserialize<Token>(responseData);
}
catch
{
MessageBox.Show("トークン取得に失敗しました。\n接続先のサーバーURL、ログイン名、パスワードを確認してください。");
return;
}
// ヘッダ設定
httpClient.DefaultRequestHeaders.Add("contentType", "application/json");
httpClient.DefaultRequestHeaders.Add("Authorization", "Bearer " + token.token);
// タイトル出力フラグ
bool writeTitle = true;
//------------------------------------------//
// プロジェクトリストの取得 //
//------------------------------------------//
requestUrl = serverUrl + "/api/project/projects?isFinished=false" ;
response = await httpClient.GetAsync(requestUrl);
responseData = await response.Content.ReadAsStringAsync();
ResponseProjects responseProject = new ResponseProjects();
responseProject = serializar.Deserialize<ResponseProjects>(responseData);
int projectNum = int.Parse(responseProject.totalCount);
for (int i = 0; i < projectNum; i++)
{
//==========================================================//
// 各プロジェクトのルート直下のサブアイテムの取得 //
//==========================================================//
requestUrl = serverUrl + "/api/workitem/workItems/" + responseProject.data[i].workItemRootFolderId + "/subItems?Fields=Id,ProjectId,ProjectName,ProjectCode,StatusTypeId,Description,ItemNumber,ActualProgress,PlannedStartDate,PlannedFinishDate,PlannedTime,ActualTime,ActualStartDate,ActualFinishDate,RemainingTime,PlannedCost,ActualCost,OutputScalePlan,OutputScaleActual,OutputUnit,OutputInfo,LeaderId,FinishDate,WorkingDayCount,SPI,TPI,CPI,IsAclInherited,CanEdit&depth=1";
response = await httpClient.GetAsync(requestUrl);
responseData = await response.Content.ReadAsStringAsync();
WorkItem[] responseGetSubitems = new WorkItem[1];
responseGetSubitems = serializar.Deserialize<WorkItem[]>(responseData);
/* 書き出し処理 */
//------------------------------------------//
// CSVファイルへデータの書き出し //
//------------------------------------------//
const string ExportFileName = "project_export.csv"; // エクスポートファイル名
StreamWriter sr = new StreamWriter(@ExportFileName, true, System.Text.Encoding.GetEncoding("shift_jis"));
if(writeTitle == true)
{
sr.WriteLine("Id" + "," + "ProjectId" + "," + "ProjectName" + "," + "ProjectCode" + "," + "StatusTypeId" + "," + "Description" + "," + "ItemNumber" + "," + "ActualProgress" + "," + "PlannedStartDate" + "," + "PlannedFinishDate" + "," + "PlannedTime" + "," + "ActualTime" + "," + "ActualStartDate" + "," + "ActualFinishDate" + "," + "RemainingTime" + "," + "PlannedCost" + "," + "ActualCost" + "," + "OutputScalePlan" + "," + "OutputScaleActual" + "," + "OutputUnit" + "," + "OutputInfo" + "," + "LeaderId" + "," + "FinishDate" + "," + "WorkingDayCount" + "," + "SPI" + "," + "TPI" + "," + "CPI" + "," + "IsAclInherited" + "," + "CanEdit");
writeTitle = false;
}
sr.WriteLine
(
string.Format(
"{0},{1},{2},{3},{4},{5},{6},{7},{8},{9},{10},{11},{12},{13},{14},{15},{16},{17},{18},{19},{20},{21},{22},{23},{24},{25},{26},{27},{28}",
responseGetSubitems[0].fields.Id,
responseGetSubitems[0].fields.ProjectId,
responseGetSubitems[0].fields.ProjectName,
responseGetSubitems[0].fields.ProjectCode,
responseGetSubitems[0].fields.StatusTypeId,
responseGetSubitems[0].fields.Description,
responseGetSubitems[0].fields.ItemNumber,
responseGetSubitems[0].fields.ActualProgress,
responseGetSubitems[0].fields.PlannedStartDate,
responseGetSubitems[0].fields.PlannedFinishDate,
responseGetSubitems[0].fields.PlannedTime,
responseGetSubitems[0].fields.ActualTime,
responseGetSubitems[0].fields.ActualStartDate,
responseGetSubitems[0].fields.ActualFinishDate,
responseGetSubitems[0].fields.RemainingTime,
responseGetSubitems[0].fields.PlannedCost,
responseGetSubitems[0].fields.ActualCost,
responseGetSubitems[0].fields.OutputScalePlan,
responseGetSubitems[0].fields.OutputScaleActual,
responseGetSubitems[0].fields.OutputUnit,
responseGetSubitems[0].fields.OutputInfo,
responseGetSubitems[0].fields.LeaderId,
responseGetSubitems[0].fields.FinishDate,
responseGetSubitems[0].fields.WorkingDayCount,
responseGetSubitems[0].fields.SPI,
responseGetSubitems[0].fields.TPI,
responseGetSubitems[0].fields.CPI,
responseGetSubitems[0].fields.IsAclInherited,
responseGetSubitems[0].fields.CanEdit
)
);
sr.Close();
}
}
public class Token
{
public string token { get; set; }
}
public class ResponseProjects
{
public string totalCount;
public ResponseProject[] data;
}
public class ResponseProject
{
public string Id;
public string workItemRootFolderId;
public ResponseProject()
{
this.Id = null;
this.workItemRootFolderId = null;
}
}
public class WorkItem
{
public Fields fields { get; set; }
}
public class Fields
{
public string Id;
public string ProjectId;
public string ProjectName;
public string ProjectCode;
public string StatusTypeId;
public string Description;
public string ItemNumber;
public string ActualProgress;
public string PlannedStartDate;
public string PlannedFinishDate;
public string PlannedTime;
public string ActualTime;
public string ActualStartDate;
public string ActualFinishDate;
public string RemainingTime;
public string PlannedCost;
public string ActualCost;
public string OutputScalePlan;
public string OutputScaleActual;
public string OutputUnit;
public string OutputInfo;
public string LeaderId;
public string FinishDate;
public string WorkingDayCount;
public string SPI;
public string TPI;
public string CPI;
public string IsAclInherited;
public string CanEdit;
public WorkItem[] SubItems;
public Fields()
{
WorkItem tmp = new WorkItem();
this.SubItems = new WorkItem[9];
this.SubItems[0] = tmp;
this.SubItems[1] = tmp;
this.SubItems[2] = tmp;
}
}