Compare commits

...

3 Commits

4 changed files with 65 additions and 28 deletions

View File

@ -2,7 +2,7 @@
"manifestVersion": 1,
"id": "enhanced-sprint-history",
"publisher": "JamesSkemp",
"version": "0.0.99",
"version": "0.0.115",
"name": "Enhanced Sprint History",
"description": "Azure DevOps Extension",
"categories": [

View File

@ -15,7 +15,7 @@ export interface ITypedWorkItem {
export interface ITypedWorkItemWithRevision {
workItem: ITypedWorkItem;
lastRevision: ITypedWorkItem | undefined;
change: string;
change: string[];
}
export interface IHubWorkItemHistory {

View File

@ -21,7 +21,19 @@ export function getIterationRelevantWorkItems(typedWorkItems: ITypedWorkItem[],
return [];
}
return typedWorkItems.filter(wi => wi.revision >= firstRevision.revision);
let typedWorkItemsRelevantToIteration = typedWorkItems.filter(wi => wi.revision >= firstRevision.revision);
if (typedWorkItemsRelevantToIteration.find(wi => wi.iterationPath !== iterationPath)) {
// Wasn't in the iteration at least once after it was.
const typedWorkItemLastTimeInIteration = typedWorkItemsRelevantToIteration.sort((a, b) => a.revision === b.revision ? 0 : a.revision < b.revision ? 1 : -1).find(wi => wi.iterationPath === iterationPath);
if (typedWorkItemLastTimeInIteration) {
const indexWhenLastInIteration = typedWorkItemsRelevantToIteration.indexOf(typedWorkItemLastTimeInIteration);
if (indexWhenLastInIteration !== 0) {
typedWorkItemsRelevantToIteration = typedWorkItemsRelevantToIteration.filter(wi => wi.revision <= typedWorkItemsRelevantToIteration[indexWhenLastInIteration - 1].revision);
}
}
}
return typedWorkItemsRelevantToIteration;
}
export function getFlattenedRelevantRevisions(workItemHistory: IHubWorkItemIterationRevisions[]): ITypedWorkItem[] {

View File

@ -45,7 +45,7 @@ export class IterationHistoryDisplay extends React.Component<IterationHistoryDis
return true;
}
const previousItem = array[i-1];
if (wi.id !== previousItem.id || isWorkItemClosed(wi) !== isWorkItemClosed(previousItem) || wi.storyPoints !== previousItem.storyPoints) {
if (wi.id !== previousItem.id || isWorkItemClosed(wi) !== isWorkItemClosed(previousItem) || (wi.storyPoints !== previousItem.storyPoints && !isWorkItemClosed(wi)) || wi.iterationPath !== previousItem.iterationPath) {
return true;
}
return false;
@ -62,34 +62,32 @@ export class IterationHistoryDisplay extends React.Component<IterationHistoryDis
let returnData: ITypedWorkItemWithRevision = {
workItem: workItem,
lastRevision: undefined,
change: ''
change: []
};
if (previousWorkItemRevisions.length === 0) {
returnData.change = 'Added';
returnData.change.push('Added');
return returnData;
}
const lastRevision = previousWorkItemRevisions[0];
returnData.lastRevision = previousWorkItemRevisions[0];
if (lastRevision.iterationPath !== workItem.iterationPath) {
returnData.change = workItem.iterationPath === selectedIterationPath ? 'Added' : 'Removed';
return returnData;
returnData.change.push(workItem.iterationPath === selectedIterationPath ? 'Added' : 'Removed');
}
if (isWorkItemClosed(lastRevision) !== isWorkItemClosed(workItem)) {
if (isWorkItemClosed(workItem)) {
returnData.change = 'Closed';
return returnData;
returnData.change.push('Closed');
} else if (isWorkItemClosed(lastRevision)) {
returnData.change = 'Reopened';
return returnData;
returnData.change.push('Reopened');
}
}
if (lastRevision.storyPoints !== workItem.storyPoints) {
returnData.change = 'Story Points Changed';
return returnData;
returnData.change.push('Story Points Changed');
}
if (returnData.change.length === 0) {
returnData.change.push('unknown');
}
returnData.change = 'unknown';
return returnData;
}
@ -117,20 +115,47 @@ export class IterationHistoryDisplay extends React.Component<IterationHistoryDis
getChangedWorkItems(getFlattenedRelevantRevisions(iterationWorkItemRevisions)).map((wi, i, a) => {
const workItemChange = getWorkItemChange(wi, i, a);
const storyClosed = isWorkItemClosed(wi);
const storyPointsChanged = workItemChange.change.indexOf('Story Points Changed') >= 0;
let addedStoryPoints = 0;
let subtractedStoryPoints = 0;
if (workItemChange.change === 'Removed') {
subtractedStoryPoints = wi.storyPoints;
} else if (workItemChange.change === 'Added') {
addedStoryPoints = wi.storyPoints;
} else if (workItemChange.change === 'Story Points Changed') {
addedStoryPoints = wi.storyPoints;
subtractedStoryPoints = workItemChange.lastRevision?.storyPoints ?? 0;
} else if (workItemChange.change === 'Reopened') {
addedStoryPoints = wi.storyPoints;
} else if (storyClosed) {
let showAddedPoints = false;
let showSubtractedPoints = false;
if (workItemChange.change.indexOf('Removed') >= 0) {
subtractedStoryPoints = wi.storyPoints;
showSubtractedPoints = true;
}
if (workItemChange.change.indexOf('Added') >= 0) {
addedStoryPoints = wi.storyPoints;
showAddedPoints = true;
}
if (workItemChange.change.indexOf('Reopened') >= 0) {
addedStoryPoints = wi.storyPoints;
showAddedPoints = true;
}
if (storyClosed) {
subtractedStoryPoints = wi.storyPoints;
showSubtractedPoints = true;
}
if (storyPointsChanged) {
if (!showAddedPoints && !showSubtractedPoints) {
addedStoryPoints = wi.storyPoints;
subtractedStoryPoints = workItemChange.lastRevision?.storyPoints ?? 0;
showAddedPoints = true;
showSubtractedPoints = true;
} else if (storyClosed) {
addedStoryPoints = wi.storyPoints;
subtractedStoryPoints += workItemChange.lastRevision?.storyPoints ?? 0;
showAddedPoints = true;
showSubtractedPoints = true;
} else {
// TODO potentially?
/*console.groupCollapsed(wi.id);
console.table(wi);
console.log(workItemChange);
console.groupEnd();*/
}
}
let changeCharacterCode = 160;
if (addedStoryPoints > subtractedStoryPoints) {
changeCharacterCode = 8593; //8599;
@ -147,9 +172,9 @@ export class IterationHistoryDisplay extends React.Component<IterationHistoryDis
<tr>
<td>{wi.changedDateFull.toLocaleString()}</td>
<td><a href={wi.url} target="_blank" title={wi.title}>{wi.id}</a><br />{wi.title}</td>
<td>{workItemChange.change}</td>
<td className="story-points increase">{addedStoryPoints !== 0 || workItemChange.change === 'Story Points Changed' ? addedStoryPoints : ''}</td>
<td className="story-points decrease">{subtractedStoryPoints !== 0 || workItemChange.change === 'Story Points Changed' || storyClosed || workItemChange.change === 'Removed' ? subtractedStoryPoints : ''}</td>
<td>{workItemChange.change.join(', ')}</td>
<td className="story-points increase">{addedStoryPoints !== 0 || showAddedPoints ? '+' + addedStoryPoints : ''}</td>
<td className="story-points decrease">{subtractedStoryPoints !== 0 || showSubtractedPoints ? '-' + subtractedStoryPoints : ''}</td>
<td className={totalStoryPointsClass}>{totalStoryPoints} {String.fromCharCode(changeCharacterCode)}</td>
</tr>
);