获取具有 TimeSpent 任务的已接受故事
Getting Accepted Stories that have Tasks with TimeSpent
我正在编写一个应用程序,该应用程序应加载已接受的故事,这些故事的任务在“花费的时间”字段中为整数。
正如我在一些测试中看到的那样,我创建的可通过 UserStory 访问的任务字段是:'Tasks', 'TaskActualTotal', 'TaskEstimateTotal', 'TaskRemainingTotal' and 'TaskStatus'
.
Tasks
有一个 _ref
属性,其中 link 到 JSON 包含此故事的所有任务。由于我使用的是 Rally API,我该如何探索它?或者有更好的方法吗?
更新:
这就是我现在所拥有的。
var storiesCall = Ext.create('Rally.data.WsapiDataStore', {
model: 'UserStory',
fetch: ['Tasks']
});
storiesCall.load().then({
success: this.loadTasks,
scope: this
});
loadTasks: function(stories) {
storiesCall = _.flatten(stories);
console.log(stories.length);
_.each(stories, function(story) {
var tasks = story.get('Tasks');
if(tasks.Count > 0) {
tasks.store = story.getCollection('Tasks', {fetch: ['Name','FormattedID','Workproduct','Estimate','TimeSpent']});
console.log(tasks.store.load().deferred);
}
else{
console.log('no tasks');
}
});
}
tasks.store.load().deferred
正在返回以下对象:
请注意,我们可以在 value[0]
上看到任务数据,但是当我尝试用 tasks.store.load().deferred.value[0]
将其包装时,它崩溃了。
有什么想法吗?
根据 WS API doc,Task 对象上的 TimeSpent 字段(根据 Rally Timesheet/Time Tracker 模块中的条目自动填充)不能用于查询,因此类似这样的内容 (TimeSpent > 0) 将不工作。
此外,UserStory 对象(在 WS API 中称为 HierarchicalRequirement)没有子任务的 TimeSpent 汇总到故事的字段,类似于子任务的 Estimate 汇总到 TaskEstimateTotal 的方式一个故事。
可以获取每个任务的 TimeSpent,然后通过访问故事的 Tasks 集合将它们相加,就像在此应用中所做的那样:
Ext.define('CustomApp', {
extend: 'Rally.app.App',
componentCls: 'app',
launch: function() {
var initiatives = Ext.create('Rally.data.wsapi.Store', {
model: 'PortfolioItem/Initiative',
fetch: ['Children']
});
initiatives.load().then({
success: this.loadFeatures,
scope: this
}).then({
success: this.loadParentStories,
scope: this
}).then({
success: this.loadChildStories,
scope: this
}).then({
success: this.loadTasks,
failure: this.onFailure,
scope: this
}).then({
success: function(results) {
results = _.flatten(results);
_.each(results, function(result){
console.log(result.data.FormattedID, 'Estimate: ', result.data.Estimate, 'WorkProduct:', result.data.WorkProduct.FormattedID, 'TimeSpent', result.data.TimeSpent );
});
this.makeGrid(results);
},
failure: function(error) {
console.log('oh, noes!');
},
scope: this
});
},
loadFeatures: function(initiatives) {
var promises = [];
_.each(initiatives, function(initiative) {
var features = initiative.get('Children');
if(features.Count > 0) {
features.store = initiative.getCollection('Children',{fetch: ['Name','FormattedID','UserStories']});
promises.push(features.store.load());
}
});
return Deft.Promise.all(promises);
},
loadParentStories: function(features) {
features = _.flatten(features);
var promises = [];
_.each(features, function(feature) {
var stories = feature.get('UserStories');
if(stories.Count > 0) {
stories.store = feature.getCollection('UserStories', {fetch: ['Name','FormattedID','Children']});
promises.push(stories.store.load());
}
});
return Deft.Promise.all(promises);
},
loadChildStories: function(parentStories) {
parentStories = _.flatten(parentStories);
var promises = [];
_.each(parentStories, function(parentStory) {
var children = parentStory.get('Children');
if(children.Count > 0) {
children.store = parentStory.getCollection('Children', {fetch: ['Name','FormattedID','Tasks']});
promises.push(children.store.load());
}
});
return Deft.Promise.all(promises);
},
loadTasks: function(stories) {
stories = _.flatten(stories);
var promises = [];
_.each(stories, function(story) {
var tasks = story.get('Tasks');
if(tasks.Count > 0) {
tasks.store = story.getCollection('Tasks', {fetch: ['Name','FormattedID','Workproduct','Estimate','TimeSpent']});
promises.push(tasks.store.load());
}
else{
console.log('no tasks');
}
});
return Deft.Promise.all(promises);
},
makeGrid:function(tasks){
var data = [];
_.each(tasks, function(task){
data.push(task.data);
});
_.each(data, function(record){
record.Story = record.WorkProduct.FormattedID + " " + record.WorkProduct.Name;
});
this.add({
xtype: 'rallygrid',
showPagingToolbar: true,
showRowActionsColumn: true,
editable: false,
store: Ext.create('Rally.data.custom.Store', {
data: data,
groupField: 'Story'
}),
features: [{ftype:'groupingsummary'}],
columnCfgs: [
{
xtype: 'templatecolumn',text: 'ID',dataIndex: 'FormattedID',width: 100,
tpl: Ext.create('Rally.ui.renderer.template.FormattedIDTemplate'),
summaryRenderer: function() {
return "Totals";
}
},
{
text: 'Name',dataIndex: 'Name'
},
{
text: 'TimeSpent',dataIndex: 'TimeSpent',
summaryType: 'sum'
},
{
text: 'Estimate',dataIndex: 'Estimate',
summaryType: 'sum'
}
]
});
}
});
我正在编写一个应用程序,该应用程序应加载已接受的故事,这些故事的任务在“花费的时间”字段中为整数。
正如我在一些测试中看到的那样,我创建的可通过 UserStory 访问的任务字段是:'Tasks', 'TaskActualTotal', 'TaskEstimateTotal', 'TaskRemainingTotal' and 'TaskStatus'
.
Tasks
有一个 _ref
属性,其中 link 到 JSON 包含此故事的所有任务。由于我使用的是 Rally API,我该如何探索它?或者有更好的方法吗?
更新:
这就是我现在所拥有的。
var storiesCall = Ext.create('Rally.data.WsapiDataStore', {
model: 'UserStory',
fetch: ['Tasks']
});
storiesCall.load().then({
success: this.loadTasks,
scope: this
});
loadTasks: function(stories) {
storiesCall = _.flatten(stories);
console.log(stories.length);
_.each(stories, function(story) {
var tasks = story.get('Tasks');
if(tasks.Count > 0) {
tasks.store = story.getCollection('Tasks', {fetch: ['Name','FormattedID','Workproduct','Estimate','TimeSpent']});
console.log(tasks.store.load().deferred);
}
else{
console.log('no tasks');
}
});
}
tasks.store.load().deferred
正在返回以下对象:
请注意,我们可以在 value[0]
上看到任务数据,但是当我尝试用 tasks.store.load().deferred.value[0]
将其包装时,它崩溃了。
有什么想法吗?
根据 WS API doc,Task 对象上的 TimeSpent 字段(根据 Rally Timesheet/Time Tracker 模块中的条目自动填充)不能用于查询,因此类似这样的内容 (TimeSpent > 0) 将不工作。
此外,UserStory 对象(在 WS API 中称为 HierarchicalRequirement)没有子任务的 TimeSpent 汇总到故事的字段,类似于子任务的 Estimate 汇总到 TaskEstimateTotal 的方式一个故事。
可以获取每个任务的 TimeSpent,然后通过访问故事的 Tasks 集合将它们相加,就像在此应用中所做的那样:
Ext.define('CustomApp', {
extend: 'Rally.app.App',
componentCls: 'app',
launch: function() {
var initiatives = Ext.create('Rally.data.wsapi.Store', {
model: 'PortfolioItem/Initiative',
fetch: ['Children']
});
initiatives.load().then({
success: this.loadFeatures,
scope: this
}).then({
success: this.loadParentStories,
scope: this
}).then({
success: this.loadChildStories,
scope: this
}).then({
success: this.loadTasks,
failure: this.onFailure,
scope: this
}).then({
success: function(results) {
results = _.flatten(results);
_.each(results, function(result){
console.log(result.data.FormattedID, 'Estimate: ', result.data.Estimate, 'WorkProduct:', result.data.WorkProduct.FormattedID, 'TimeSpent', result.data.TimeSpent );
});
this.makeGrid(results);
},
failure: function(error) {
console.log('oh, noes!');
},
scope: this
});
},
loadFeatures: function(initiatives) {
var promises = [];
_.each(initiatives, function(initiative) {
var features = initiative.get('Children');
if(features.Count > 0) {
features.store = initiative.getCollection('Children',{fetch: ['Name','FormattedID','UserStories']});
promises.push(features.store.load());
}
});
return Deft.Promise.all(promises);
},
loadParentStories: function(features) {
features = _.flatten(features);
var promises = [];
_.each(features, function(feature) {
var stories = feature.get('UserStories');
if(stories.Count > 0) {
stories.store = feature.getCollection('UserStories', {fetch: ['Name','FormattedID','Children']});
promises.push(stories.store.load());
}
});
return Deft.Promise.all(promises);
},
loadChildStories: function(parentStories) {
parentStories = _.flatten(parentStories);
var promises = [];
_.each(parentStories, function(parentStory) {
var children = parentStory.get('Children');
if(children.Count > 0) {
children.store = parentStory.getCollection('Children', {fetch: ['Name','FormattedID','Tasks']});
promises.push(children.store.load());
}
});
return Deft.Promise.all(promises);
},
loadTasks: function(stories) {
stories = _.flatten(stories);
var promises = [];
_.each(stories, function(story) {
var tasks = story.get('Tasks');
if(tasks.Count > 0) {
tasks.store = story.getCollection('Tasks', {fetch: ['Name','FormattedID','Workproduct','Estimate','TimeSpent']});
promises.push(tasks.store.load());
}
else{
console.log('no tasks');
}
});
return Deft.Promise.all(promises);
},
makeGrid:function(tasks){
var data = [];
_.each(tasks, function(task){
data.push(task.data);
});
_.each(data, function(record){
record.Story = record.WorkProduct.FormattedID + " " + record.WorkProduct.Name;
});
this.add({
xtype: 'rallygrid',
showPagingToolbar: true,
showRowActionsColumn: true,
editable: false,
store: Ext.create('Rally.data.custom.Store', {
data: data,
groupField: 'Story'
}),
features: [{ftype:'groupingsummary'}],
columnCfgs: [
{
xtype: 'templatecolumn',text: 'ID',dataIndex: 'FormattedID',width: 100,
tpl: Ext.create('Rally.ui.renderer.template.FormattedIDTemplate'),
summaryRenderer: function() {
return "Totals";
}
},
{
text: 'Name',dataIndex: 'Name'
},
{
text: 'TimeSpent',dataIndex: 'TimeSpent',
summaryType: 'sum'
},
{
text: 'Estimate',dataIndex: 'Estimate',
summaryType: 'sum'
}
]
});
}
});