プロジェクトを立ち上げ、WBSを作成する
概要
プロジェクトを作成する際には、以下の処理を実行する必要があります。
- プロジェクトの作成
- メンバーの登録
- WBSの作成
年度や期の切り替わりなど、多数のプロジェクトを同時期に開始する場面において、上記処理を一つずつ実施するには非常に手間がかかります。この場面でも、Web APIを活用すれば簡単にプロジェクトを作成し、すぐに運用を開始できます。
利用場面
- 新規でプロジェクトを立ち上げる
- プロジェクトに複数のメンバをまとめて追加する
使用するAPI
以下 のAPIを使用します。
- プロジェクトの追加
- ワークアイテムの追加
- ワークアイテムの更新
サンプルスクリプト
言語に合わせて以下のタブを選択し、参照してください。
- JavaScript
- C#
入力情報
上記のサンプルスクリプトで使用するプロジェクトメンバー追加用のアカウント情報ファイルを以下のリンク先から参照できます。
//======================================================================
// プロジェクトを新規に作成する
//======================================================================
//======================================================================
// 外部ファイル(ライブラリ、他ファイル)のインクルード
//======================================================================
const https = require('https'); // HTTPS通信を利用する場合に設定する
const http = require('http'); // HTTP通信を利用する場合に設定する
const axios = require('axios');
const fs = require('fs');
//======================================================================
// 共通部の定義
//======================================================================
// APIを呼び出すURLを設定する
const baseURLString = 'http://yourserver/TimeTrackerNX/api';
//======================================================================
// メイン処理
//======================================================================
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';
var projectId;
// プロジェクト作成 (リクエストに1つ分のプロジェクト情報を設定して送信)
try {
const { data } = await request.post(
sendUrl,
{
name : 'サンプルプロジェクト1',
manager : '岡本 直哉',
code : 'SAMPLE-001',
plannedStartDate : '2019-04-01',
plannedFinishDate : '2020-03-31'
}
);
// 戻り値のプロジェクトIDを取得(この後のWBSの追加に利用)
projectId = data.id;
} catch (e) {
console.log(e);
}
// 新規作成したプロジェクトのルートワークアイテム(WBSの追加先)を取得する。
sendUrl = '/project/projects/' + projectId;
var workItemRootId;
try {
const { data } = await request.get(
sendUrl,
{
params:{}
}
);
workItemRootId = data[0].workItemRootFolderId;
} catch (e) {
console.log(e);
}
// コピー元になるワークアイテム(例えば標準WBS)の情報を設定する
var wbsProject_projectId = 1; // コピー元のプロジェクトID
var wbsProject_workItemId = '3,7,11,15,26'; // コピー元のワークアイテムID
// ワークアイテムの追加(リクエストに複製するワークアイテムの情報を設定して送信)
sendUrl = '/workItem/workItems/'+ workItemRootId +'/subItems/duplicate';
try {
const { data } = await request.post(
sendUrl,
{
fromProjectId : wbsProject_projectId,
fromItemIds : wbsProject_workItemId
}
);
} catch (e) {
console.log(e);
}
// プロジェクトにメンバーを追加する
sendUrl = '/project/projects/' + projectId;
// JSON形式のデータ(例:後述のaddMember.json)を入力とする
const filepath = './addMember.json';
var addMemberData = JSON.parse(fs.readFileSync(filepath, {encoding: "utf-8"}));
// プロジェクトメンバーの追加(リクエストに追加するユーザーの情報を設定して送信)
try {
const { data } = await request.put(
sendUrl,
{
memberChange:{
adds : addMemberData
}
}
);
} catch (e) {
console.log(e);
}
}
入力情報
上記のサンプルスクリプトで使用する新規プロジェクト追加用のアカウント情報ファイルを以下のリンク先から参照できます。
private async void Execute_Click(object sender, EventArgs e)
{
//==========================================//
// CSVを入力にデータを取得 //
//==========================================//
String[] newMemberData = null;
List<string> lists = new List<string>();
StreamReader sr = new StreamReader(@"newProjectData.csv", System.Text.Encoding.GetEncoding("shift_jis"));
{
//タイトル行の回避(1行目はデータにしない)
string line = sr.ReadLine();
while (!sr.EndOfStream)
{
line = sr.ReadLine();
newMemberData = line.Split(',');
lists.AddRange(newMemberData);
}
}
//======================================================//
// 必要な変数等の定義 //
//======================================================//
JavaScriptSerializer serializar = new JavaScriptSerializer();
var httpClient = new HttpClient();
// 要素数 5 (name / manager / code / plannedStartDate / plannedFinishDate)
const int ProjectDataMemberNum = 5;
string serverUrl = "http://WIN-Q1OR68LM6RG.mshome.net/TimeTrackerNX/";
string loginname = "okamoto";
string password = "";
string projectId = null;
// トークンの取得
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);
//==========================================//
// 新規 プロジェクトの追加 //
//==========================================//
//------------------------------------------//
// プロジェクトを追加 //
//------------------------------------------//
for (int i = 0; i < lists.Count; i = i + ProjectDataMemberNum)
{
var Member = new Dictionary<string, string>
{
{"name",lists[i]},
{"manager",lists[i+1]},
{"code",lists[i+2]},
{"plannedStartDate",lists[i+3] },
{"plannedFinishDate",lists[i+4]},
};
// プロジェクト追加処理
requestUrl = serverUrl + "/api/project/projects";
jsonData = serializar.Serialize(Member);
content = new StringContent(jsonData, Encoding.UTF8, "application/json");
response = await httpClient.PostAsync(requestUrl, content);
responseData = await response.Content.ReadAsStringAsync();
string[] addedMemberIds=null;
string[] addedUserGroupIds=null;
ResponseProject responseProject = new ResponseProject();
responseProject = serializar.Deserialize<ResponseProject>(responseData);
projectId = responseProject.Id;
//------------------------------------------//
// ルートアイテムの取得 //
//------------------------------------------//
string workItemRootFolderId = null;
requestUrl = serverUrl + "/api/project/projects/" + projectId;
response = await httpClient.GetAsync(requestUrl);
responseData = await response.Content.ReadAsStringAsync();
var project = serializar.Deserialize<Project[]>(responseData);
workItemRootFolderId = project[0].workItemRootFolderId;
//------------------------------------------//
// WBSのコピー・追加 //
//------------------------------------------//
requestUrl = serverUrl + "/api/workItem/workItems/" + workItemRootFolderId + "/subItems/duplicate";
RequestWorkItem requestWorkItem = new RequestWorkItem();
requestWorkItem.fromProjectId = "6";
requestWorkItem.fromItemIds = "126";
string workItemId;
Items items = new Items();
ResponseWorkitem responseWorkitem = new ResponseWorkitem();
jsonData = serializar.Serialize(requestWorkItem);
content = new StringContent(jsonData, Encoding.UTF8, "application/json");
response = await httpClient.PostAsync(requestUrl, content);
responseData = await response.Content.ReadAsStringAsync();
responseWorkitem = serializar.Deserialize<ResponseWorkitem>(responseData);
workItemId = responseWorkitem.items[0].id;
//------------------------------------------//
// ユーザーの割り当て //
//------------------------------------------//
requestUrl = serverUrl + "/api/workItem/workItems/" + workItemId;
AssignmentChange assignmentChange = new AssignmentChange();
assignmentChange.adds[0].partyId = "21";
PropagateToChildrenField field = new PropagateToChildrenField();
ProjectMemberAssignment assignmentData = new ProjectMemberAssignment(assignmentChange, field);
jsonData = serializar.Serialize(assignmentData);
content = new StringContent(jsonData, Encoding.UTF8, "application/json");
response = await httpClient.PutAsync(requestUrl, content);
responseData = await response.Content.ReadAsStringAsync();
}
}
// Tokenレスポンス用のクラス
public class Token
{
public string token { get; set; }
}
public class ResponseProject
{
public string Id;
public string[] addedMemberIds;
public string[] addedUserGroupIds;
public ResponseProject()
{
this.Id = null;
}
}
public class Member
{
public string partyId;
public string name;
public string englishName;
public string partyType;
public string projectId;
public string projectUserGroupIds;
public DateTime createdAt;
public string createdBy;
public DateTime updatedAt;
public string updatedBy;
public string id;
public bool isDeleted;
public string code;
public double unitCost;
public string organizationId;
public string organizationName;
}
public class UserGroup
{
public string id;
public string name;
public string englishName;
public string code;
public string description;
public string treePath;
public int treeDepth;
public bool isProjectPrivate;
public string privateProjectId;
public UserGroup[] subGroups;
public double displayOrder;
public bool isDeleted;
public DateTime createdAt;
public string createdBy;
public DateTime updatedAt;
public string updatedBy;
}
public class WorkCalendar
{
public string specifiedWorkDate;
public string workDayOfWeek;
}
public class ProjectData
{
public string name { get; set; }
public string code { get; set; }
public string description { get; set; }
public string managerId { get; set; }
public string managerName { get; set; }
public string organizationId { get; set; }
public string organizationName { get; set; }
public string workItemRootFolderId { get; set; }
public string profileId { get; set; }
public string baseProfileId { get; set; }
public string baseProfileName { get; set; }
public string baseProfileVersionName { get; set; }
public Member members { get; set; }
public UserGroup userGroups { get; set; }
public bool isLocked { get; set; }
public DateTime lockedAt { get; set; }
public bool isDeleted { get; set; }
public bool isFinished { get; set; }
public DateTime plannedStartDate { get; set; }
public DateTime plannedFinishDate { get; set; }
public double standardUnitCost { get; set; }
public WorkCalendar workCalendar { get; set; }
public string aclId { get; set; }
public string metadata { get; set; }
public string projectCategories { get; set; }
public DateTime createdAt { get; set; }
public string createdBy { get; set; }
public DateTime updatedAt { get; set; }
public string updatedBy { get; set; }
public ProjectData(string workItemRootFolderId)
{
this.workItemRootFolderId = workItemRootFolderId;
}
}
public class Project
{
public string workItemRootFolderId { get; set; }
}
public class Projects
{
public Project[] project;
public Projects(Project[] project)
{
this.project = project;
}
}
public class RequestWorkItem
{
public string fromProjectId;
public string fromItemIds;
}
public class AssignmentChange
{
public AssignmentAdd[] adds;
public AssignmentChange()
{
AssignmentAdd tmp = new AssignmentAdd();
this.adds = new AssignmentAdd[1];
this.adds[0] = tmp;
}
}
public class AssignmentAdd
{
public string partyType;
public string partyId;
public int plannedTime;
public AssignmentAdd()
{
this.partyType = "User";
}
}
public class PropagateToChildrenField
{
public string fields;
public PropagateToChildrenField()
{
this.fields = "Assignments";
}
}
public class ProjectMemberAssignment
{
public AssignmentChange assignmentChange;
public PropagateToChildrenField propagateToChildren;
public ProjectMemberAssignment(AssignmentChange assignmentChange, PropagateToChildrenField propagateToChildren)
{
this.assignmentChange = assignmentChange;
this.propagateToChildren = propagateToChildren;
}
}
public class ResponseWorkitem
{
public Items[] items;
public ResponseWorkitem()
{
Items tmp = new Items();
this.items = new Items[1];
this.items[0] = tmp;
}
}
public class Items
{
public string id;
}