如何跟踪谁在 TFS 工作项上输入了评论

发布于 2024-07-19 08:20:37 字数 1045 浏览 4 评论 0原文

当处理其他错误跟踪系统中的问题时,有多种方法可以向项目添加注释,这些注释清楚地标有作者的用户名和输入的日期。 我正在 TFS 的工作项中寻找类似的功能。 有这样的功能吗?

我们目前使用的系统允许我们通过热键将当前时间和用户名粘贴到多行文本字段中。 所有用户都知道将该信息粘贴到他们键入的内容上方。 虽然这是手动的,但它是可以接受的且简单的。 例如:

5/1/2009 1:20:00 am - AManagr-
Defered to next version, and here's why...

4/24/2009 1:20:00 am - ADev - 
QA machine had out of date XYZ gizmo component. Here's the convoluted way this can happen... blah blah... This is difficult to fix.

4/22/2009 1:20:00 am - QAGuy - 
I can't save reports to PDF files.

我使用过的其他工具(也许是 Mantis?)内置了“注释”功能。所以我不会忘记在评论中添加我的名字,或者知道新注释是否位于字段的顶部或底部等等...

手动输入您的姓名和日期/时间不是一个(好的)选择。 但是,点击一个键或工具栏按钮就可以了。

我并不是在寻求将这样的长格式“注释”分解为多个特定的单独字段的建议。 另外,我知道工作项上的历史记录选项卡,但这还不够。 谁写了什么、什么时候写的需要清楚,并且与文本的观点相同。

更新

想象一下几个团队成员正在研究一个问题。 它们都向工作项添加信息,每个都向同一字段附加更多文本。 您如何轻松知道谁添加了哪些部分?

历史记录日志显示每个用户的更改的一行,甚至显示字段的更改。 但那是在另一个屏幕上,很难在头脑中解析它显示的数据。

他们可以“签署”文本的每个部分 - 但如果没有该工具的帮助,这会很痛苦。

也许 Stackoverflows 的评论功能也是一个很好的例子。

When working on an issue in other bug-tracking systems there are ways to add notes to items that are clearly marked with the authors user name and the date entered. I'm looking for a similar feature in TFS's work items. Is there such a feature?

We currently use a system that allows us to hit a hot-key to paste the current time and username into the multi-line text fields. All users know to paste that info in above what they type. While this is manual, it's acceptable and easy. For example:

5/1/2009 1:20:00 am - AManagr-
Defered to next version, and here's why...

4/24/2009 1:20:00 am - ADev - 
QA machine had out of date XYZ gizmo component. Here's the convoluted way this can happen... blah blah... This is difficult to fix.

4/22/2009 1:20:00 am - QAGuy - 
I can't save reports to PDF files.

Other tools I've used (Mantis maybe?) had a "Notes" feature baked in. So I couldn't forget to put my name on comments, or know if new notes go to the top or bottom of a field, etc...

Manually typing your name and the date/time isn't a (good) option. But, hitting a single key, or toolbar button would be OK.

I'm not looking for advice on decomposing long-form "notes" like this into multiple specific individual fields. Also, I am aware of the history tab on work items, but this isn't sufficient. Who wrote what and when needs to be clear and in the same view as the text.

Update

Imagine several team members researching an issue. They all add info to the work item, each appending more text to the same field. How do you easily know who added what part?

The History log shows a line for each user's change and even shows the field's change. But that is on another screen and is hard to mentally parse the data it shows.

They could "sign" each part of the text - but this is a pain without help from the tool.

Maybe Stackoverflows comments feature is a good example as well.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

离线来电— 2024-07-26 08:20:37

是的你可以。 TFS 工作项是可定制的。 在此版本中没有我想要的那么多,但你可以做你想做的。

让我们用以下字段定义来尝试一下。 注释日期和注释作者是只读的,并从系统获取默认值。 注释字段是 HTML,您可以在其中放置任何您想要的内容。 您可以在 TFS 进程编辑器

 <FIELD reportable="dimension" type="DateTime" name="Notes Date" refname="System.VSTS.Notes.Date">
    <DEFAULT from="clock" />
    <READONLY not="[Global]\Team Foundation Administrators" />
  </FIELD>
  <FIELD reportable="dimension" type="String" name="Notes Author" refname="System.VSTS.Notes.Author">
    <DEFAULT from="currentuser" />
    <READONLY not="[Global]\Team Foundation Administrators" />
  </FIELD>
  <FIELD type="HTML" name="Notes" refname="System.VSTS.Notes" />
</FIELDS>

当然,您仍然需要向表单添加控件。

您可以尝试的另一件事是仅保留 Notes 字段并注册 WorkItemChanged 事件,并编写一个 Web 服务来使用日期和作者更新 Notes 字段。 更改日期和更改日期字段将为您提供此信息。 您可以在 Brian A. Randell 撰写的这篇文章中了解可用的活动以及如何订阅它们 - Team Foundation System Event Service

[WebService(Namespace = "http://mynamespace.com/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class UpdateWorkItem : System.Web.Services.WebService
{
    private static TeamFoundationServer _Tfs;
    private static WorkItemStore _WorkItemStore;

    private static List<WorkItem> _ChangedWorkItems = new List<WorkItem>();
  
    [SoapDocumentMethod(Action = "http://schemas.microsoft.com/TeamFoundation/2005/06/Services/Notification/03/Notify", RequestNamespace = "http://schemas.microsoft.com/TeamFoundation/2005/06/Services/Notification/03")]
    [WebMethod]
    public void Notify(string eventXml, string tfsIdentityXml)
    {

        EventLog.WriteEntry("TFS Services", "Log Started: Notify Webmethod");


        // Load the recieved XML into a XMLDocument
        XmlDocument eventXmlDoc = new XmlDocument();
        eventXmlDoc.LoadXml(eventXml);
        XmlElement eventData = eventXmlDoc.DocumentElement;

        // Validate event data
        if (eventData != null)
        {
            // Get Work Item id from event data
            int id = GetWorkItemId(eventData);

            //EventLog.WriteEntry("TFS Services", String.Format("eventXmlDoc {0}", eventXmlDoc.InnerXml));
            EventLog.WriteEntry("TFS Services", String.Format("Got Id {0}", id));
            string changedby = GetWorkItemChangedBy(eventData);
            EventLog.WriteEntry("TFS Services", String.Format("Got changedby {0}", changedby));
            if (changedby != "TFSSERVICE")
            {
                //Add a 15 second delay in order to make sure all workitems are saved first before starting to update them
                Thread.Sleep(15000);
                EventLog.WriteEntry("TFS Services", "Calling UpdateWorkItemInternal");
                UpdateWorkItemInternal(id);
            }
        }
    }

    private int GetWorkItemId(XmlElement eventData)
    {
        return Convert.ToInt32(eventData.SelectSingleNode("CoreFields/IntegerFields/Field[ReferenceName='System.Id']/NewValue").InnerText);
    }

    private string GetWorkItemChangedBy(XmlElement eventData)
    {
        return Convert.ToString(eventData.SelectSingleNode("CoreFields/StringFields/Field[ReferenceName='System.ChangedBy']/NewValue").InnerText);
    }

    private static void UpdateWorkItemInternal(int id)
    {
        //Connect To TFS Server 
        EventLog.WriteEntry("TFS Services", string.Format("Updating Work Item {0}", id));
        _Tfs = TeamFoundationServerFactory.GetServer("TeamServer");

        _WorkItemStore = (WorkItemStore)_Tfs.GetService(typeof(WorkItemStore));
        WorkItem workItem = _WorkItemStore.GetWorkItem(id);

        switch ((string)workItem.Fields["System.WorkItemType"].Value)
        {
            case "Bug":
                UpdateNotes(workItem);
                break;
            default:
                break;
        }

        foreach (WorkItem item in _ChangedWorkItems)
        {
            if (item.IsDirty)
            {
                foreach (Field field in item.Fields)
                {
                    if (!field.IsValid)
                    {
                        Console.Write("Not valid");
                    }
                }
                EventLog.WriteEntry("TFS Services", string.Format("Saving WorkItem: {0}", item.Id));
                try
                {
                    item.Save();
                }
                catch (Exception ex)
                {
                }
            }
        }

        _ChangedWorkItems.Clear();
    }
    
    private static void UpdateNotes(WorkItem workItem)
    {
       Field notes = workitem.Fields["System.VSTS.Notes"];
       if (notes != null)
       {
         notes = string.Format("{0} - {1}", workItem.ChangedDate, workItem.ChangedBy);
       } 

       if (workItem.IsDirty)
       {
           if (!_ChangedWorkItems.Contains(workItem))
           {
               _ChangedWorkItems.Add(workItem);
           }
       }
    }
 }

这只是快速而肮脏的,从我现有的代码中进行了一些复制和粘贴,因此请仔细检查确保我没有输入错误。

Yes you can. TFS work items are customizable. Not as much as I would like in this version but you can do what you want.

Lets take a stab at it with the following field definitions. The Notes Date and Notes Author are readonly and get their default values from the system. Notes field is HTML and you can put whatever you want in there. You can do this in the TFS Process Editor.

 <FIELD reportable="dimension" type="DateTime" name="Notes Date" refname="System.VSTS.Notes.Date">
    <DEFAULT from="clock" />
    <READONLY not="[Global]\Team Foundation Administrators" />
  </FIELD>
  <FIELD reportable="dimension" type="String" name="Notes Author" refname="System.VSTS.Notes.Author">
    <DEFAULT from="currentuser" />
    <READONLY not="[Global]\Team Foundation Administrators" />
  </FIELD>
  <FIELD type="HTML" name="Notes" refname="System.VSTS.Notes" />
</FIELDS>

Of course you will still need to add controls to your form.

Another thing you can try is only keep the Notes field and register for a WorkItemChanged event and write a webservice to update the notes field with the Date and Author. Changed BY and Changed Date fields will give you this information. You can learn about the available events and how to subscribe to them in this article by Brian A. Randell - Team Foundation System Event Service

[WebService(Namespace = "http://mynamespace.com/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class UpdateWorkItem : System.Web.Services.WebService
{
    private static TeamFoundationServer _Tfs;
    private static WorkItemStore _WorkItemStore;

    private static List<WorkItem> _ChangedWorkItems = new List<WorkItem>();
  
    [SoapDocumentMethod(Action = "http://schemas.microsoft.com/TeamFoundation/2005/06/Services/Notification/03/Notify", RequestNamespace = "http://schemas.microsoft.com/TeamFoundation/2005/06/Services/Notification/03")]
    [WebMethod]
    public void Notify(string eventXml, string tfsIdentityXml)
    {

        EventLog.WriteEntry("TFS Services", "Log Started: Notify Webmethod");


        // Load the recieved XML into a XMLDocument
        XmlDocument eventXmlDoc = new XmlDocument();
        eventXmlDoc.LoadXml(eventXml);
        XmlElement eventData = eventXmlDoc.DocumentElement;

        // Validate event data
        if (eventData != null)
        {
            // Get Work Item id from event data
            int id = GetWorkItemId(eventData);

            //EventLog.WriteEntry("TFS Services", String.Format("eventXmlDoc {0}", eventXmlDoc.InnerXml));
            EventLog.WriteEntry("TFS Services", String.Format("Got Id {0}", id));
            string changedby = GetWorkItemChangedBy(eventData);
            EventLog.WriteEntry("TFS Services", String.Format("Got changedby {0}", changedby));
            if (changedby != "TFSSERVICE")
            {
                //Add a 15 second delay in order to make sure all workitems are saved first before starting to update them
                Thread.Sleep(15000);
                EventLog.WriteEntry("TFS Services", "Calling UpdateWorkItemInternal");
                UpdateWorkItemInternal(id);
            }
        }
    }

    private int GetWorkItemId(XmlElement eventData)
    {
        return Convert.ToInt32(eventData.SelectSingleNode("CoreFields/IntegerFields/Field[ReferenceName='System.Id']/NewValue").InnerText);
    }

    private string GetWorkItemChangedBy(XmlElement eventData)
    {
        return Convert.ToString(eventData.SelectSingleNode("CoreFields/StringFields/Field[ReferenceName='System.ChangedBy']/NewValue").InnerText);
    }

    private static void UpdateWorkItemInternal(int id)
    {
        //Connect To TFS Server 
        EventLog.WriteEntry("TFS Services", string.Format("Updating Work Item {0}", id));
        _Tfs = TeamFoundationServerFactory.GetServer("TeamServer");

        _WorkItemStore = (WorkItemStore)_Tfs.GetService(typeof(WorkItemStore));
        WorkItem workItem = _WorkItemStore.GetWorkItem(id);

        switch ((string)workItem.Fields["System.WorkItemType"].Value)
        {
            case "Bug":
                UpdateNotes(workItem);
                break;
            default:
                break;
        }

        foreach (WorkItem item in _ChangedWorkItems)
        {
            if (item.IsDirty)
            {
                foreach (Field field in item.Fields)
                {
                    if (!field.IsValid)
                    {
                        Console.Write("Not valid");
                    }
                }
                EventLog.WriteEntry("TFS Services", string.Format("Saving WorkItem: {0}", item.Id));
                try
                {
                    item.Save();
                }
                catch (Exception ex)
                {
                }
            }
        }

        _ChangedWorkItems.Clear();
    }
    
    private static void UpdateNotes(WorkItem workItem)
    {
       Field notes = workitem.Fields["System.VSTS.Notes"];
       if (notes != null)
       {
         notes = string.Format("{0} - {1}", workItem.ChangedDate, workItem.ChangedBy);
       } 

       if (workItem.IsDirty)
       {
           if (!_ChangedWorkItems.Contains(workItem))
           {
               _ChangedWorkItems.Add(workItem);
           }
       }
    }
 }

This is just quick and dirty with some copy and paste from my existing code so review it carefully to make sure I didn't introduce a typo.

青春有你 2024-07-26 08:20:37

我很好奇“历史记录”选项卡的问题? 它在灰色条中包含日期/时间和用户名,其下方紧接着是他们发表的评论。

如果他们没有发表评论,但对工作项进行了其他更改,则会出现一个名为“显示更改的字段”的折叠标题。 但评论始终可见。

更新

您可以创建自定义工作项控件,以提供您自己的工作项历史记录视图。

http://msdn.microsoft.com/en-us/library/bb286959。 aspx

另请参阅TFS 工作项跟踪自定义控件

I'm curious about the issue with the History tab? It contains the date/time and the user's name in a grey bar with the comment they made immediately below it.

If they didn't make a comment, but made other changes to the work item there will be a collapsed heading called Show Changed Fields. The comment is always visible though.

Update

You could create a custom work item control that provides your own view of the work item's history.

http://msdn.microsoft.com/en-us/library/bb286959.aspx

See also TFS Work Item Tracking Custom Controls

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文