2015年7月30日 星期四

TFS 實作新增待處理項目階層(Portfolio管理)

 ALM   Team Foundation Server


背景


因為主管需要做專案組合Portfolio的管理,因此在MSDN找到這一篇TFS延伸功能的文章:

實作後,發現 對於實務上的Portfolio管理幫助不大,
只是單純在原本的需求(Product Backlog)→工作(或稱Sprint Backlog) 的階層再往上加了一層 
(恩,想了一下,就暫且在本文稱之為Portfolio Backlog XD)

先看一下預期的結果, 待處理項目」中,在功能及需求的階層之上,多了一個我們客製化的最上層: Initiatives


在右邊可看出整個的工作階層 : Initiatives→特殊功能→需求→工作

PS. 小組報表也可顯示相關資訊~


環境

l   Windows 7 Enterprise
l   8 GB Ram
l   Team Foundation Server 2013 Update 5 RC


實作


列出所有TFSWIT (Work Item Type)

開啟「VS2013 開發人員命令提示字元」,輸入以下指令:

witadmin listwitd /collection:http://localhost:8080/tfs/DefaultCollection /p:CMMI-Project

l   /collection : TFS專案集合Url
l   /p : 指定該專案集合下的哪一個專案

列出的結果(TFS 2013 CMMI預設專案範本為例) :
Bug
程式碼檢閱要求
程式碼檢閱回應
特殊功能
意見要求
意見回應
工作
問題
變更要求
風險
需求
檢閱
測試案例
共用步驟
測試計劃
測試套件


匯出TFS的設定檔

匯出相關設定的XML
l   Process config : 流程設定

witadmin exportprocessconfig
/collection:http://localhost:8080/tfs/defaultcollection
/p:CMMI-Project
/f:"C:\temp\Config.xml"

l   WITD : 工作項目設定

witadmin exportwitd
/collection:http://localhost:8080/tfs/defaultcollection
/p:CMMI-Project
/f:"C:\temp\Feature.xml"
/n:特殊功能
n   /n : 只要指定這次要修改的WIT

l   Categories : 功能類別設定

witadmin exportcategories
/collection:http://localhost:8080/tfs/defaultcollection
/p:CMMI-Project
/f:"C:\temp\Categories.xml"


Feature.xml為範本,建立新的工作項目設定(WIT definition)

l   <WORKITEMTYPE name="特殊功能"> 改為 <WORKITEMTYPE name="Initiative">


l    
<WorkItemTypeFilters FilterType="include">
<Filter WorkItemType="需求" />
</WorkItemTypeFilters>
改為
<WorkItemTypeFilters FilterType="include">
<Filter WorkItemType="功能" />
</WorkItemTypeFilters>



修改Categories.xml (功能類別設定) : Category列表加入新工作項目類型及目錄。

在裡面加上
<CATEGORY refname="Microsoft.InitiativeCategory" name="Initiative類別">
    <DEFAULTWORKITEMTYPE name="Initiative" />
</CATEGORY>



修改Config.xml (流程設定檔)

在流程設定檔的<PortfolioBacklogs>內,加入
<PortfolioBacklog category="Microsoft.InitiativeCategory" pluralName="Initiatives" singularName="Initiative">
      <AddPanel>
        <Fields>
          <Field refname="System.Title" />
        </Fields>
      </AddPanel>
      <Columns>
        <Column width="100" refname="System.WorkItemType" />
        <Column width="400" refname="System.Title" />
        <Column width="100" refname="System.State" />
        <Column width="50" refname="Microsoft.VSTS.Common.BusinessValue" />
        <Column width="200" refname="System.Tags" />
      </Columns>
      <States>
        <State type="Proposed" value="已提議" />
        <State type="InProgress" value="作用中" />
        <State type="InProgress" value="已解決" />
        <State type="Complete" value="已關閉" />
      </States>
    </PortfolioBacklog>

並將原本 <PortfolioBacklog category="Microsoft.FeatureCategory" … >
加入一個父階層:
parent="Microsoft.InitiativeCategory"





匯入所有已修改的TFS設定檔

witadmin importwitd
/collection:http://tp-chenghanlin:8080/tfs/defaultcollection
/p:CMMI-Project
/f:C:/temp/Feature.xml

witadmin importcategories
/collection:http://tp-chenghanlin:8080/tfs/defaultcollection
/p:CMMI-Project
/f:C:/temp/Categories.xml

witadmin importprocessconfig
/collection:http://tp-chenghanlin:8080/tfs/defaultcollection
/p:CMMI-Project
/f:C:/temp/Config.xml



結果 (待處理項目)



Reference



















2015年7月28日 星期二

[MVC] IEnumerable Extensions

 C#   MVC  


背景

MVC中,因時常會使用到將預設選項以IEnumerable<SelectListItem> 的型態BindingDropDownList Helper,搜尋找到一版滿完整的IEnumerableExtensions,因此紀錄一下使用方式。



練習


View

<div class="col-md-10">
@Html.DropDownListFor(model => model.Sex, (IEnumerable<SelectListItem>)ViewData["SexSelectList"],
new { htmlAttributes = new { @class = "form-control" } })

@Html.ValidationMessageFor(model => model.Sex, "", new { @class = "text-danger" })
</div>


IEnumerable Extensions

/// <summary>
    /// IEnumerable Extensions
    /// </summary>
    public static class IEnumerableExtensions
    {
        /// <summary>
        ///
        /// </summary>
        /// <param name="values"></param>
        /// <returns></returns>
        public static string JoinString(this IEnumerable<string> values)
        {
            return JoinString(values, ",");
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="values"></param>
        /// <param name="split"></param>
        /// <returns></returns>
        public static string JoinString(this IEnumerable<string> values, string split)
        {
            var result = values.Aggregate(string.Empty, (current, value) => current + (split + value));
            result = result.TrimStart(split.ToCharArray());
            return result;
        }

        /// <summary>
        /// Do somthing for each item in the IEnumerable
        /// </summary>
        /// <typeparam name="T">Type</typeparam>
        /// <param name="source">Source IEnumerable</param>
        /// <param name="action">Action for doing something</param>
        /// <returns>IEnumerable</returns>
        public static IEnumerable<T> Each<T>(this IEnumerable<T> source, Action<T> action)
        {
            if (source != null)
            {
                foreach (var item in source)
                {
                    action(item);
                }
            }
            return source;
        }

        /// <summary>
        /// Convert IEnumerable to IList<SelectListItem>
        /// </summary>
        /// <typeparam name="T">Type</typeparam>
        /// <param name="source">IEnumerable</param>
        /// <param name="text">Func for display text of IEnumerable</param>
        /// <param name="value">Func for value of IEnumerable</param>
        /// <returns>IList of SelectListItem</returns>
        public static IList<SelectListItem> ToSelectList<T>(
            this IEnumerable<T> source, Func<T, object> text, Func<T, object> value, bool isOrder = false)
        {
            return source.ToSelectList(text, value, null, null);
        }

        /// <summary>
        /// Convert IEnumerable to IList<SelectListItem>
        /// </summary>
        /// <typeparam name="T">Type</typeparam>
        /// <param name="source">IEnumerable</param>
        /// <param name="text">Func for display text of IEnumerable</param>
        /// <param name="value">Func for value of IEnumerabl</param>
        /// <param name="optionalText">Extra option</param>
        /// <returns>IList of SelectListItem</returns>
        public static IList<SelectListItem> ToSelectList<T>(
            this IEnumerable<T> source, Func<T, object> text, Func<T, object> value,
            string optionalText, bool isOrder = false)
        {
            return source.ToSelectList(text, value, null, optionalText, isOrder);
        }

        /// <summary>
        /// Convert IEnumerable to IList<SelectListItem>
        /// </summary>
        /// <typeparam name="T">Type</typeparam>
        /// <param name="source">IEnumerable</param>
        /// <param name="text">Func for display text of IEnumerable</param>
        /// <param name="value">Func for value of IEnumerabl</param>
        /// <param name="selected">Func for Seleced item</param>
        /// <returns>IList of SelectListItem</returns>
        public static IList<SelectListItem> ToSelectList<T>(
            this IEnumerable<T> source, Func<T, object> text, Func<T, object> value,
            Func<T, bool> selected, bool isOrder = false)
        {
            return source.ToSelectList(text, value, selected, null);
        }


        /// <summary>
        /// Convert IEnumerable to IList<SelectListItem>
        /// </summary>
        /// <typeparam name="T">Type</typeparam>
        /// <param name="source">IEnumerable</param>
        /// <param name="text">Func for display text of IEnumerable</param>
        /// <param name="value">Func for value of IEnumerabl</param>
        /// <param name="selected">Func for Seleced item</param>
        /// <param name="optionalText">Extra option</param>
        /// <returns>IList of SelectListItem</returns>
        public static IList<SelectListItem> ToSelectList<T>(
            this IEnumerable<T> source, Func<T, object> text, Func<T, object> value,
            Func<T, bool> selected, string optionalText, bool isOrder = false)
        {
            return source.ToSelectList(text, value, selected, optionalText, string.Empty, isOrder);
        }


        /// <summary>
        /// Core function for EnumerableExtension.cs
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source"></param>
        /// <param name="text"></param>
        /// <param name="value"></param>
        /// <param name="selected"></param>
        /// <param name="optionalText"></param>
        /// <param name="optionalValue"></param>
        /// <returns></returns>
        private static IList<SelectListItem> ToSelectList<T>(
            this IEnumerable<T> source, Func<T, object> text, Func<T, object> value,
            Func<T, bool> selected, string optionalText, string optionalValue, bool isOrder = false)
        {
            var items = new List<SelectListItem>();

            //預設會先加入optionalText
            if (!string.IsNullOrEmpty(optionalText))
            {
                items.Insert(0, new SelectListItem() { Text = optionalText, Value = optionalValue });
            }

            if (source == null)
            {
                return items;
            }


            foreach (var entity in source)
            {
                var item = new SelectListItem();
                item.Text = text(entity).ToString();
                item.Value = value(entity).ToString();
                if (selected != null)
                {
                    item.Selected = selected(entity);
                }

                if (item.Value != optionalValue)
                {
                    items.Add(item);
                }
            }

            if (isOrder)
            {
                items = items.OrderBy(d => d.Text).ToList();           
            }

            return items;
        }
    }


 Controller

Controller中自行建立(或拉回)有”Male””Female”的資料如下:
Key
Value
1
Male
2
Female

UnitTest codes in controller (Of course you have to comment out the non-testing codes by urself) :
public ActionResult Create()
{


            //Given "Text" and "Value" to the IList<SelectListItem>
            ViewData["SexSelectList"] =
                sexList.AsEnumerable().ToSelectList(x => x.Value, x => x.Key);
            //Set isOrder = true
            ViewData["SexSelectList"] =
                sexList.AsEnumerable().ToSelectList(x => x.Value, x => x.Key, isOrder: true);
            //Set the default selected value in IList<SelectListItem>
            ViewData["SexSelectList"] =
                sexList.AsEnumerable().ToSelectList(x => x.Value, x => x.Key, x => x.Value.Equals("Female"));
            //Add a new item : "Please select ..." to IList<SelectListItem>
            ViewData["SexSelectList"] =
                sexList.AsEnumerable().ToSelectList(x => x.Value, x => x.Key, "Please select ...");
            //Set the default selected value and add a new item in IList<SelectListItem>
            ViewData["SexSelectList"] =
                sexList.AsEnumerable().ToSelectList(x => x.Value, x => x.Key, x => x.Value.Equals("Male"), "Please select ...");
            //Do an action for every item in IList<SelectListItem>
            ViewData["SexSelectList"] = sexList.AsEnumerable()
                .Each(x => x.Value = (x.Value.Equals("Male") ? (x.Value + "()") : (x.Value + "()")))
                .ToSelectList(x => x.Value, x => x.Key);


            return View();
}

//Given "Text" and "Value" to the IList<SelectListItem>

//Set isOrder = true

//Set the default selected value in IList<SelectListItem>

//Add a new item : "Please select ..." to IList<SelectListItem>

//Set the default selected value and add a new item in IList<SelectListItem>


//Do an action for every item in IList<SelectListItem>