Rally 如何按故事时数获取投资组合传奇项目的状态?

Rally 如何按故事时数获取投资组合传奇项目的状态?,rally,Rally,有人知道一种简单的方法,可以在与“Portfolioitem/Saga”相关的报道时间内完成百分比吗?CA中现有的portfolio items应用程序仅显示按故事计划估算完成的百分比,我也想获得任务时数的详细信息 我已经编写了以下自定义应用程序来包含数据,但是我的表在返回数据之前被加载。请帮忙 Ext.define('Rally.app.PortfolioGrid', { extend: 'Rally.app.App', componentCls

有人知道一种简单的方法,可以在与“Portfolioitem/Saga”相关的报道时间内完成百分比吗?CA中现有的portfolio items应用程序仅显示按故事计划估算完成的百分比,我也想获得任务时数的详细信息

我已经编写了以下自定义应用程序来包含数据,但是我的表在返回数据之前被加载。请帮忙

Ext.define('Rally.app.PortfolioGrid', {
            extend: 'Rally.app.App',
            componentCls: 'app',

            launch: function() {
                console.log('Working');
                this._loadComboBox();
            },

            _loadComboBox : function() {
                this.searchComboBox = Ext.create('Rally.ui.combobox.ArtifactSearchComboBox', {
                    fieldLabel: 'Saga',
                    noEntryText: '',
                    emptyText: 'Enter saga ID or keyword...',
                    //grow: true,
                    //allowNoEntry: false,
                    storeConfig: {
                        autoLoad: true,
                        models: ['PortfolioItem/Saga']
                    },
                    listeners: {
                        select: function(combobox, records) {
                            this._loadData();
                        },
                        scope: this
                    }
                });
                this.add(this.searchComboBox);
            },

            _loadData: function () {
                var selectedID = this.searchComboBox.getRecord().get('FormattedID');
                console.log('Selected Saga', selectedID);
                var myFilters = [
                    {
                        property: 'FormattedID',
                        operation: '=',
                        value: selectedID
                    },
                ];
                if (this.myStore) {
                    console.log('store exists');
                    this.myStore.setFilter(myFilters);
                    this.myStore.load();
                  // create store
                } else {    
                    this.myStore = Ext.create('Rally.data.wsapi.Store', {
                        model: 'PortfolioItem/Saga',
                        autoLoad: true,
                        fetch: ['FormattedID', 'Name', 'Children', 'Release', 'State', 'PercentDoneByStoryPlanEstimate', 'PercentDoneByStoryCount', 'Project', 'Owner'],
                        filters: myFilters,
                        listeners: {
                            load: this._onDataLoaded,
                            scope: this
                        }
                    });
                }           
            }, 

            _onDataLoaded: function(store, data){
                var features = [];
                var pendingstories = data.length;
                Ext.Array.each(data, function(feature) {
                            var f  = {
                                FormattedID: feature.get('FormattedID'),
                                Name: feature.get('Name'),
                                _ref: feature.get("_ref"),
                                State: (feature.get('State') && feature.get('State').Name) || ' ',
                                Project: (feature.get('Project') && feature.get('Project').Name) || ' ',
                                Owner: (feature.get('Owner') && feature.get('Owner')._refObjectName) || 'No Owner',
                                PercentDoneByStoryPlanEstimate: Math.floor(feature.get('PercentDoneByStoryPlanEstimate') * 100) + '%',
                                PercentDoneByStoryCount: Math.floor(feature.get('PercentDoneByStoryCount') * 100) + '%',
                                Children: [],
                                totalPlannedHours: 0,
                                totalHoursRemaining: 0,
                                totalHoursCompleted: 0,
                                Percentage: 0                                   
                            };

                            var sagaFeatures = feature.getCollection('Children');
                            sagaFeatures.load({
                                fetch: ['FormattedID', 'Parent', 'UserStories', 'DirectChildrenCount'],
                                callback: function(records, operation, success){
                                    Ext.Array.each(records, function(child){ 
                                            var s = {
                                                FormattedID: child.get('FormattedID'),
                                            };  

                                            if (child.get('DirectChildrenCount') > 0) {
                                                child.getCollection('UserStories').load({
                                                fetch: ['FormattedID', 'TaskEstimateTotal', 'TaskRemainingTotal'],
                                                callback: function(records, operation, success) {
                                                    Ext.Array.each(records, function(us) {
                                                        f.totalPlannedHours += us.get('TaskEstimateTotal');
                                                        f.totalHoursRemaining += us.get('TaskRemainingTotal');
                                                        f.totalHoursCompleted = f.totalPlannedHours - f.totalHoursRemaining

                                                        console.log("Total Hours Completed", f.totalHoursRemaining)

                                                        f.Percentage = Math.floor((f.totalHoursCompleted / f.totalPlannedHours) * 100) + '%'; 

                                                    }, this);


                                                }, scope: this});
                                            }                                               
                                            f.Children.push(s);

                                    }, this);

                                },
                                scope: this
                            });

                            features.push(f);

                            --pendingstories;
                            if (pendingstories === 0) {
                                this._createGrid(features);
                            } 

                }, this);

                this._createGrid(features);
            },

            _createGrid: function(features) {
                var myCustomStore = Ext.create('Rally.data.custom.Store', {
                    data: features
                    //pageSize: 100,
                });
                if (!this.grid) {
                    this.grid = this.add({
                    xtype: 'rallygrid',
                    flex: 1,
                    store: myCustomStore,
                    columnCfgs: [
                        {
                           text: 'Formatted ID', dataIndex: 'FormattedID', xtype: 'templatecolumn',
                            tpl: Ext.create('Rally.ui.renderer.template.FormattedIDTemplate')
                        },
                        {
                            text: 'Name', dataIndex: 'Name'
                        },
                        {
                            text: 'State', dataIndex: 'State'
                        },
                        {
                            text: 'Saga Features', dataIndex: 'Children', 
                            renderer: function(value) {
                                var html = [];
                                Ext.Array.each(value, function(child){
                                    html.push('<a href="' + Rally.nav.Manager.getDetailUrl(child) + '">' + child.FormattedID + '</a>')
                                });
                                return html.join(', ');
                            }
                        },
                        {
                            text: 'Percent Done By Story Plan Estimate', dataIndex: 'PercentDoneByStoryPlanEstimate'
                        },
                        {
                            text: 'Percent Done By Story Count', dataIndex: 'PercentDoneByStoryCount'
                        },
                        {
                            text: 'Total Planned Hours', dataIndex: 'totalPlannedHours'
                        },
                        {
                            text: 'Total Completed Hours', dataIndex: 'totalHoursCompleted'
                        },
                        {
                            text: 'Percent Done by Story Hours', dataIndex: 'Percentage'
                        }, 
                        {
                            text: 'Project', dataIndex: 'Project'
                        },
                        {
                            text: 'Owner', dataIndex: 'Owner'
                        }
                    ]

                    });
                }
                else {
                    this.grid.reconfigure(myCustomStore);
                }
            }
    });
Ext.define('Rally.app.PortfolioGrid'{
扩展:“Rally.app.app”,
组件CLS:“应用程序”,
启动:函数(){
console.log('Working');
这是。_loadComboBox();
},
_loadComboBox:函数(){
this.searchComboBox=Ext.create('Rally.ui.combobox.ArtifactSearchComboBox'{
fieldLabel:“传奇”,
noEntryText:“”,
emptyText:'输入saga ID或关键字…',
//成长:是的,
//allowNoEntry:错误,
storeConfig:{
自动加载:对,
型号:['PortfolioItem/Saga']
},
听众:{
选择:函数(组合框,记录){
这是。_loadData();
},
范围:本
}
});
this.add(this.searchComboBox);
},
_loadData:函数(){
var selectedID=this.searchComboBox.getRecord().get('FormattedID');
console.log('selectedsaga',selectedID);
var myFilters=[
{
属性:“FormattedID”,
操作:'=',
值:selectedID
},
];
if(this.myStore){
log('storeexists');
this.myStore.setFilter(myFilters);
this.myStore.load();
//创建存储
}否则{
this.myStore=Ext.create('Rally.data.wsapi.Store'{
型号:“PortfolioItem/Saga”,
自动加载:对,
获取:['FormattedID','Name','Children','Release','State','PercentDoneByStoryPlanEstimate','PercentDoneByStoryCount','Project','Owner'],
过滤器:myFilters,
听众:{
加载:这个。加载后,
范围:本
}
});
}           
}, 
_onDataLoaded:函数(存储、数据){
var特征=[];
var pendingstories=data.length;
Ext.Array.each(数据、函数(功能){
变量f={
FormattedID:feature.get('FormattedID'),
名称:feature.get('Name'),
_ref:feature.get(“\u ref”),
State:(feature.get('State')&&feature.get('State').Name)|,
项目:(feature.get('Project')&&feature.get('Project').Name)|,
所有者:(feature.get('Owner')&&feature.get('Owner')。_-reobjectname)| |'无所有者',
PercentDoneByStoryPlanEstimate:Math.floor(feature.get('PercentDoneByStoryPlanEstimate')*100)+'%,
PercentDoneByStoryCount:Math.floor(feature.get('PercentDoneByStoryCount')*100)+'%',
儿童:[],
计划总小时数:0,
剩余总小时数:0,
完成的总小时数:0,
百分比:0
};
var sagaFeatures=feature.getCollection('Children');
sagaFeatures.load({
获取:['FormattedID','Parent','UserStories','DirectChildrenCount'],
回调:函数(记录、操作、成功){
Ext.Array.each(记录,函数(子){
变量s={
FormattedID:child.get('FormattedID'),
};  
if(child.get('DirectChildrenCount')>0){
child.getCollection('UserStories').load({
获取:['FormattedID','TaskEstimateTotal','TaskRemainingTotal'],
回调:函数(记录、操作、成功){
Ext.Array.each(记录、函数(us){
f、 totalPlannedHours+=us.get('TaskEstimateTotal');
f、 totalHoursRemaining+=us.get('TaskRemainingTotal');
f、 totalHoursCompleted=f.totalPlannedHours-f.TotalHours剩余
console.log(“完成的总小时数”,f.TotalHours剩余时间)
f、 百分比=数学楼层((f.总完工小时数/f.总计划小时数)*100)+'%;
},这个);
},范围:本});
}
this.myStore = Ext.create('Rally.data.wsapi.Store', {
    model: 'UserStory',
    autoLoad: true,
    limit: Infinity,
    pageSize: 2000,
    fetch: ['FormattedID', 'Name', 'TaskEstimateTotal', 'TaskRemainingTotal', 'Feature', 'Parent', 'Children'],
    filters: [{ property: 'Feature.Parent', operator: '=', value: this.searchComboBox.getRecord().get('_ref')}],
    listeners: {
        load: this._onDataLoaded,
        scope: this
    }
});
_onDataLoaded: function(store) {
    var sagas = {};
    var stories = store.getRange();
    _.each(stories, function(story) {
        var feature = store.get('Feature'),
            saga = feature.Parent;

        if (!sagas[saga._ref]) {
            sagas[saga._ref] = Ext.clone(saga);
        }
        var featureChildren = sagas[saga._ref].Children.values = sagas[saga._ref].Children.values || {};
        if (!featureChildren[feature._ref]) {
            featureChildren[feature._ref] = Ext.clone(feature);
        }

        var storyChildren = featureChildren[feature._ref].UserStories.values = featureChildren[feature._ref].UserStories.values || {};
        if (!storyChildren[story._ref]) {
            storyChildren[story._ref] = Ext.clone(story);
        }
    });

    //now you have an object of sagas, just needs to be an array for your custom store data
    var customStoreData = _.values(sagas);
    this._createGrid(customStoreData);
}