2013-11-01 1 views
1

카드가 첫 번째 상태가 된 이후로 일수를 계산하는 방법이 있습니까? kanban 상태에 사용자 정의 필드를 사용한다고 말할 수 있습니다. 1,2,3,4 카드가 주 3에 있다면 1 번 이후 얼마나 걸렸습니까?집회 첫 날부터 칸반 # 일?

답변

1

자동화하거나 항목을 신고하는 방법이 확실하지 않지만 미국/독일 문제를 검토하는 경우 개정 내역을 잠시 살펴보십시오.

상태의 변경 사항은 기록에 기록되어야합니다.

0

역사적인 데이터에 액세스하려면 Lookback API를 사용하십시오.

Lookback API를 사용하면 과거에 보인 작업 항목 또는 작업 항목 모음을 볼 수 있습니다. 이는 오브젝트의 현재 상태를 제공 할 수 있지만 실행 기록 데이터가없는 WS API를 직접 사용하는 것과는 다 (니다.

LBAPI 문서는 here

여기에 사용할 수있는 간판 상태 이야기의 그리드를 구축 한 예이다. 이야기의 행을 두 번 클릭하면, 시간이 이야기는 칸반이 계산 상태 세에 보냈다 및 그리드는 값을 표시하는 내장되어 있습니다 :

enter image description here

Ext.define('CustomApp', { 
    extend: 'Rally.app.App', 
    componentCls: 'app', 
    launch: function(){ 
     var x = Ext.create('Rally.data.lookback.SnapshotStore', { 
       fetch : ['Name','c_Kanban','_UnformattedID', '_TypeHierarchy'], 
       filters : [{ 
        property : '__At', 
        value : 'current' 
       }, 
       { 
        property : '_TypeHierarchy', 
        value : 'HierarchicalRequirement' 
       }, 
       { 
        property : '_ProjectHierarchy', 
        value : 22222 
       }, 
       { 
       property : 'c_Kanban', //get stories with Kanban state 
       operator : 'exists', 
       value : true 
       } 
       ], 
       hydrate: ['_TypeHierarchy', 'c_Kanban'], 
       listeners: { 
        load: this.onStoriesLoaded, 
        scope: this 
       } 
       }).load({ 
        params : { 
         compress : true, 
         removeUnauthorizedSnapshots : true 
        } 
       }); 

    }, 
    //make grid of stories with Kanban state 
    onStoriesLoaded: function(store, data){ 
     var that = this; 
     var stories = []; 
     var id; 
     Ext.Array.each(data, function(record) { 
      var artifactType = record.get('_TypeHierarchy'); 
      if (artifactType[artifactType.length - 1] == "HierarchicalRequirement") { 
       id = 'US' + record.get('_UnformattedID'); 
      } else if (artifactType[artifactType.length - 1] == "Defect") { 
       id = 'DE' + record.get('_UnformattedID'); 
      } 
      stories.push({ 
       Name: record.get('Name'), 
       FormattedID: id, 
       UnformattedID: record.get('_UnformattedID'), 
       c_Kanban: record.get('c_Kanban') 
      }); 
      console.log(stories); 
      }); 

      var myStore = Ext.create('Rally.data.custom.Store', { 
       data: stories 
      }); 

      if (!this.down('#allStoriesGrid')) { 
       this.add({ 
        xtype: 'rallygrid', 
        id: 'allStoriesGrid', 
        store: myStore, 
        width: 500, 
        columnCfgs: [ 
         { 
          text: 'Formatted ID', dataIndex: 'FormattedID', 
         }, 
         { 
          text: 'Name', dataIndex: 'Name', flex: 1, 
         }, 
         { 
          text: 'Kanban', dataIndex: 'c_Kanban' 
         } 
        ], 
        listeners: { 
         celldblclick: function(grid, td, cellIndex, record, tr, rowIndex){ 
          id = grid.getStore().getAt(rowIndex).get('UnformattedID'); 
          console.log('id', id); 
          that.getStoryModel(id);  //to eventually build a grid of Kanban allowed values 
          } 
        } 
       }); 
      }else{ 
       this.down('#allStoriesGrid').reconfigure(myStore); 
      } 
    }, 

    getStoryModel:function(id){ 
     console.log('get story model'); 
     var that = this; 
     this.arr=[]; 
     //get a model of user story 
     Rally.data.ModelFactory.getModel({ 
      type: 'User Story', 
      context: { 
       workspace: '/workspace/11111', 
       project: 'project/22222'  
      }, 
      success: function(model){ 
       //Get store instance for the allowed values 
       var allowedValuesStore = model.getField('c_Kanban').getAllowedValueStore(); 
       that.getDropdownValues(allowedValuesStore, id); 
      } 

     }); 
    }, 


    getDropdownValues:function(allowedValuesStore, id){ 
     var that = this; 
     //load data into the store 
     allowedValuesStore.load({ 
      scope: this, 
      callback: function(records, operation, success){ 
       _.each(records, function(val){ 
        //AllowedAttributeValue object in WS API has StringValue 
        var v = val.get('StringValue'); 
        that.arr.push(v); 
       }); 
       console.log('arr', this.arr); 
       that.getStoryById(id); //former makeStore 
      } 
     }); 
    }, 

    getStoryById:function(id){ 
     var that = this; 
     var snapStore = Ext.create('Rally.data.lookback.SnapshotStore', { 
      fetch: ['c_Kanban', 'Blocked'], 
      hydrate:['c_Kanban','Blocked'], 
      filters : [ 
       { 
        property : '_UnformattedID', 
        value : id //15 
       } 
      ], 
      sorters:[ 
       { 
        property : '_ValidTo', 
        direction : 'ASC' 
       } 
      ] 
     }); 
     snapStore.load({ 
      params: { 
       compress: true, 
       removeUnauthorizedSnapshots : true 
      }, 
      callback : function(records, operation, success) { 
       that.onDataLoaded(records, id); 
      } 
     }); 
    }, 

    onDataLoaded:function(records, id){ 
     var times = []; 
     var measure = 'second'; 

     //-----------------------backlog 

     var backlog = _.filter(records, function(record) { 
        return record.get('c_Kanban') === 'backlog'; 
     }); 
     console.log('backlog',backlog); 
     var cycleTimeFromBacklogToInProgress = ''; 
     if (_.size(backlog) > 0) { 
      var backlog1 = _.first(backlog); 
      var backlog2 = _.last(backlog); 
      var backlogDate1 = new Date(backlog1.get('_ValidFrom')); 
      if (backlog2.get('_ValidTo') === "9999-01-01T00:00:00.000Z") { //infinity 
       backlogDate2 = new Date(); //now 
      } 
      else{ 
       var backlogDate2 = new Date(backlog2.get('_ValidTo')); 
      } 

      cycleTimeFromBacklogToInProgress = Rally.util.DateTime.getDifference(backlogDate2,backlogDate1, measure); 
     } 
     times.push(cycleTimeFromBacklogToInProgress); 
     //console.log(cycleTimeFromBacklogToInProgress); 

     //----------------------in progress 


     var inProgress = _.filter(records, function(record) { 
        return record.get('c_Kanban') === 'in-progress'; 
     }); 
     console.log('in-progress',inProgress); 
     var cycleTimeFromInProgressToDone = ''; 
     if (_.size(inProgress) > 0) { 
      var inProgress1 = _.first(inProgress); 
      var inProgress2 = _.last(inProgress); 
      var inProgressDate1 = new Date(inProgress1.get('_ValidFrom')); 
      if (inProgress2.get('_ValidTo') === "9999-01-01T00:00:00.000Z") { //infinity 
       inProgressDate2 = new Date(); //now 
      } 
      else{ 
       var inProgressDate2 = new Date(inProgress2.get('_ValidTo')); 
      } 
      cycleTimeFromInProgressToDone = Rally.util.DateTime.getDifference(inProgressDate2,inProgressDate1, measure); 
     } 
     times.push(cycleTimeFromInProgressToDone); 
     //console.log(cycleTimeFromInProgressToDone); 


     //------------------------done 

     var done = _.filter(records, function(record) { 
        return record.get('c_Kanban') === 'done'; 
     }); 
     console.log('done',done); 
     var cycleTimeFromDoneToReleased = ''; 
     if (_.size(done) > 0) { 
      var done1 = _.first(done); 
      var done2 = _.last(done); 
      var doneDate1 = new Date(done1.get('_ValidFrom')); 
      if (done2.get('_ValidTo') === "9999-01-01T00:00:00.000Z") { //infinity 
       doneDate2 = new Date(); //now 
      } 
      else{ 
       var doneDate2 = new Date(done2.get('_ValidTo')); 
      } 
      cycleTimeFromDoneToReleased = Rally.util.DateTime.getDifference(doneDate2,doneDate1, measure); 
     } 
     times.push(cycleTimeFromDoneToReleased); 
     //console.log(cycleTimeFromDoneToReleased); 


     /********** 
     skip first '' element of the this.arr and last 'released' element of this.arr because 
     do not care for cycle times in first and last kanban states 
     Originally: arr ["", "backlog", "in-progress", "done", "released"] ,shorten to: ["backlog", "in-progress", "done"] 

     **********/ 

     this.arrShortened = _.without(this.arr, _.first(this.arr),_.last(this.arr)) ; 
     console.log('this.arrShortened with first and last skipped', this.arrShortened); //["backlog", "in-progress", "done"] 

     cycleTimes = _.zip(this.arrShortened, times); 
     //console.log('cycleTimes as multi-dimentional array', cycleTimes); 

     cycleTimes = _.object(cycleTimes); 
     //console.log('cycleTimes as object', cycleTimes); //cycleTimes as object Object {backlog: 89, in-progress: 237, done: 55} 

     var cycleTimesArray = []; 

     cycleTimesArray.push(cycleTimes); 

     console.log('cycleTimesArray',cycleTimesArray); 

     var store = Ext.create('Rally.data.custom.Store',{ 
      data: cycleTimesArray, 
      pageSize: 100 
     }); 


     var columnConfig = []; 
     _.each(cycleTimes,function(c,key){ 
      var columnConfigElement = _.object(['text', 'dataIndex', 'flex'], ['time spent in ' + key, key, 1]); 
      columnConfig.push(columnConfigElement); 
     }); 

     var title = 'Kanban cycle time for US' + id + ' in ' + measure + 's' 
     if (!this.grid) { 
      this.grid = this.add({ 
       xtype: 'rallygrid', 
       title: title, 
       width: 500, 
       itemId: 'grid2', 
       store: store, 
       columnCfgs: columnConfig 
      }); 
     } 
     else{ 
      this.down('#grid2').reconfigure(store); 
     } 
    } 

}); 
+0

닉 내가에 배치 할 수 있습니다이 코드를 사용자 정의 HTML 응용 프로그램? 복사 한 후 저장하면 코드가 표시됩니다. – JimD

+0

이것은 js 파일입니다. 전체 html 파일을 복사해야합니다. 이 코드 (및 전체 HTML)의 변형은 다음에서 찾을 수 있습니다. https://github.com/nmusaelian-rally/caclulate-cycle-time-kanban/tree/master/deploy이 코드는 지원되는 카탈로그 앱이 아니므로, 코딩 예제를 추가로 사용자 정의 할 수 있습니다. – nickm