DevOps Parent Child Hierarchy in Power BI

Last modified date

Azure DevOps is built around the pattern than items have child items and so a backlog view will show the items in a hierarchy. When the work items some into Power BI that is not shown. So this gives me the chance to use one of my favourite resources from SQLBI, DAX Patterns and their Parent-Child pattern.

Screen grab from DevOps showing a backlog view of nested items

I am using the Agile process so the hierarchy is Epic – Feature – User Story – Task. I have made sure my project includes all the layers correctly. You can work around oddities but for this post we will assume all layers of the items are as we expect.

Power BI and DevOps

This post is part of a series

DAX Patterns – Parent Child Hierarchy

Dax Patterns logo

DAX patterns comes in multiple formats, a book, a website and videos. They are written by SQLBI and have saved me hours and hours. For this post we are going to adapt the Parent-Child pattern to DevOps data. I am going to use the report built in the previous post in this series. The pattern is fully explained here https://www.daxpatterns.com/parent-child-hierarchies

Adding the Path

The pattern uses a function called path. This function takes an row id and the parent row id and walks up the path until it reaches a row with no parent. Work Items in DevOps have WorkItemID and ParentWorkItemID. So following the instructions we add a column to the WorkItems table.

ItemPath = PATH(WorkItems[WorkItemId],WorkItems[ParentWorkItemId])
WorkItems table table showing WorkitemID, Title, ParentWorkItemId and the new column ItemPath

The ItemPath gives us the list of IDs in the hierarchy above the current row. It does requires the path to be complete so it might not work if you have filtered out some of the items.

Adding DevOps Hierarchy Level Columns

From the above post these are the Level 1 to Level 4 columns, we just need to swap out the right column names. We also know the the names of our levels from DevOps so we can use slightly more meaningful names, Level 1 = Epic, Level 2 = Feature etc. Add the 4 columns using the pattern below, copied straight from the SQLBI post.

Epic = 
VAR LevelNumber = 1
VAR LevelKey = PATHITEM ( WorkItems[ItemPath], LevelNumber, INTEGER )
VAR LevelName = LOOKUPVALUE ( WorkItems[Title], WorkItems[WorkItemId], LevelKey )
VAR Result = LevelName
RETURN
    Result
Table of data showing ItemPath, Epic, Feature, User Story and Task columns.

Adding a Matrix

The quickest way to see the hierarchy in action is to add a matrix visual. Into the rows we put the levels of the hierarchy to whatever levels we want. Into the values I put # Work Items. It looks great we can expand the different levels and see the 72 work items split out into features etc.

Picture of the matrix showing the 4 fields added to Rows and Work Items measure added to Values and the blank lines that appear in the Matrix at the top of each expanded section

But we get blank rows at the top of each section. Not to worry of course our friends at SQLBI have a solution.

Fixing the measures

In order for this to work we need to compare the maximum path length with the level being displayed. The path length we add as a column Depth to Work Items and then add a measure MaxRowDepth. Then using ISINSCOPE function we calculate the display depth in a measure called ItemBrowseDepth.

Column Added to WorkItems table

Depth = PATHLENGTH( WorkItems[ItemPath] )

2 Measures Added to WorkItems table

MaxRowDepth = MAX( WorkItems[Depth] )

ItemBrowseDepth =
    ISINSCOPE ( WorkItems[Epic] ) 
    + ISINSCOPE ( WorkItems[Feature] )
    + ISINSCOPE ( WorkItems[User Story] )
    + ISINSCOPE ( WorkItems[Task] )

Now we can add measures that use these values to decide if to display the row or not. The logic being we only show rows where the browse depth is less than or equal to the max row depth. So I fix the # Work Items using the example from the SQLBI Pattern.

# Work Items =
VAR Val = COUNTROWS ( WorkItems )
VAR ShowRow = [ItemBrowseDepth] <= [MaxRowDepth]
VAR Result = IF ( ShowRow, Val )
RETURN
    Result

This now fixes the matrix to not show blank rows

Matrix with no blank rows

Conclusion on Parent Child Hierarchy

This makes the DevOps data easy to report on. The measures roll up the data to the different levels so the total hours can be calculated etc. Handling DevOps plans with missing layers is hard work, I’ve done it where some User Stories were children of the Epic. That made the pattern clunky so I’d probably push back now and insist the DevOps plan was kept clean.

Over 20 year experience at being passionate about training, solving problems and loving a new challenge especially in the Microsoft's Power Platform suite.