Michael, it doesn't seem to be filtering the Description:
a <!DOCTYPE html>
<html>
<head>
<title>Revisions by User</title>
<script type="text/javascript" src="/apps/2.0rc3/sdk.js"></script>
<script type="text/javascript">
//YYYY-MM-DD 6-29 to 8-30
Rally.onReady(function() {
Ext.define('CustomApp', {
extend: 'Rally.app.App',
componentCls: 'app',
_artifacts: [],
launch: function() {
var today = new Date().toISOString();
var iterationStart = new Date('29 June 2017').toISOString();
var iterationFinish = new Date('30 August 2017').toISOString();
var iteration = Rally.data.wsapi.Filter.and([
{property: 'Iteration.StartDate',operator: '>=',value: iterationStart },
{property: 'Iteration.EndDate',operator: '<=',value: iterationFinish }
]);
var description = Rally.data.wsapi.Filter.or([
{property: 'Description',operator: 'contains',value: 'ACCEPTANCE CRITERIA'},
{property: 'Description',operator: 'contains',value: 'DESCRIPTION'}
]);
var that = this;
var widgetPanel = Ext.create("Ext.Panel", {
itemId: "widget",
layout: {
type: "hbox",
align: "middle"
},
items: [{
xtype: "rallyprojectpicker",
itemId: "projectPicker",
fieldLabel: "Select Project",
workspace: this.getContext().getWorkspace()._ref,
value: this.getContext().getProject()._ref,
listeners: {
change: this.onProjectSelected,
scope: this
},
width: 300,
mqrgin: 20
}, {
xtype: "rallyiterationcombobox",
itemId: "iterationPicker",
fieldLabel: "Select Iteration:",
listeners: {
ready: this.onIterationSelected,
select: this.onIterationSelected,
scope: this
},
width: 300,
margin: 20
}]
});
this.add(widgetPanel), this.add({
xtype: "container",
itemId: "reportContainer"
})
var artifacts = Ext.create('Rally.data.wsapi.Store', {
model: 'UserStory',
fetch: ['ObjectID', 'FormattedID', 'Name', 'Iteration', 'RevisionHistory', 'Revisions', 'Description', 'User'],
autoLoad: true,
limit: 10000,
pageSize: 1000,
filters: [iteration.and(description)]
});
artifacts.load().then({
success: this._getRevHistoryModel,
scope: this
}).then({
success: this._onRevHistoryModelCreated,
scope: this
}).then({
success: this._onModelLoaded,
scope: this
}).then({
success: this._stitchDataTogether,
scope: this
}).then({
success: function(results) {
that._makeGrid(results);
},
failure: function() {
console.log("oh noes!");
}
});
},
onProjectSelected: function(combobox) {
console.log("project", combobox.getSelectedRecord().get("_ref")), Ext.ComponentQuery.query("#iterationPicker")[0].setProject(combobox.getSelectedRecord().get("_ref"))
},
onIterationSelected: function(combobox) {
console.log("iteration", combobox.getValue())
},
_getRevHistoryModel: function(artifacts) {
this._artifacts = artifacts;
return Rally.data.ModelFactory.getModel({
type: 'RevisionHistory'
});
},
_onRevHistoryModelCreated: function(model) {
var that = this;
var promises = [];
_.each(this._artifacts, function(artifact) {
var ref = artifact.get('RevisionHistory')._ref;
console.log(artifact.get('FormattedID'), ref);
promises.push(model.load(Rally.util.Ref.getOidFromRef(ref)));
});
return Deft.Promise.all(promises);
},
_onModelLoaded: function(histories) {
var promises = [];
_.each(histories, function(history) {
var revisions = history.get('Revisions');
revisions.store = history.getCollection('Revisions', {
fetch: ['User', 'Description', 'CreationDate', 'RevisionNumber']
});
promises.push(revisions.store.load());
});
return Deft.Promise.all(promises);
},
_stitchDataTogether: function(revhistories) {
var that = this;
var artifactsWithRevs = [];
_.each(that._artifacts, function(artifact) {
artifactsWithRevs.push({
artifact: artifact.data
});
});
var i = 0;
_.each(revhistories, function(revisions) {
artifactsWithRevs[i].revisions = revisions;
i++;
});
return artifactsWithRevs;
},
_makeGrid: function(artifactsWithRevs) {
console.log(artifactsWithRevs);
this.add({
xtype: 'rallygrid',
showPagingToolbar: true,
showRowActionsColumn: false,
editable: false,
store: Ext.create('Rally.data.custom.Store', {
data: artifactsWithRevs,
pageSize: 1000
}),
columnCfgs: [{
text: 'FormattedID',
dataIndex: 'artifact',
renderer: function(value) {
return '<a href="https://rally1.rallydev.com/#/detail/userstory/' + value.ObjectID + '" target="_blank">' + value.FormattedID + '</a>';
}
}, {
text: 'Name',
dataIndex: 'artifact',
renderer: function(value) {
return value.Name;
}
}, {
text: 'Iteration',
dataIndex: 'artifact',
renderer: function(value) {
return value.Iteration.Name;
}
}, {
text: 'Revision author',
dataIndex: 'revisions',
renderer: function(value) {
var html = [];
_.each(value, function(rev) {
html.push(rev.data.User._refObjectName);
});
return html.join('</br></br>');
}
}, {
text: 'Revision # and description',
dataIndex: 'revisions',
flex: 1,
renderer: function(value) {
var html = [];
_.each(value, function(rev) {
html.push("REV# " + rev.data.RevisionNumber + ": " + rev.data.Description.replace('changed from [', 'changed from :<blockquote class="textBefore">').replace('] to', '</blockquote>to:<br/><blockquote class="textAfter">') + '</blockquote>');
});
return html.join('</br>'); //</br></br>
}
}]
});
}
});
Rally.launchApp('CustomApp', {
name: "Revisions by User",
parentRepos: ""
});
});
</script>
<style type="text/css">
.textBofore{margin-left: 50px;text-decoration: line-through;}
.textAfter{margin-left: 50px;}
blockquote {
background: #f9f9f9;
border-left: 10px solid #ccc;
margin: 1.5em 10px;
padding: 0.5em 10px;
quotes: "\201C""\201D""\2018""\2019";
}
blockquote:before {
color: #ccc;
content: open-quote;
font-size: 4em;
line-height: 0.1em;
margin-right: 0.25em;
vertical-align: -0.4em;
}
blockquote p {
display: inline;
}
</style>
</head>
<body>
</body>
</html>