Skip to content

feat(grafana): jira sprint based dashboards#8574

Open
Bhoopalan1999 wants to merge 8 commits intoapache:mainfrom
Bhoopalan1999:main
Open

feat(grafana): jira sprint based dashboards#8574
Bhoopalan1999 wants to merge 8 commits intoapache:mainfrom
Bhoopalan1999:main

Conversation

@Bhoopalan1999
Copy link
Copy Markdown

@Bhoopalan1999 Bhoopalan1999 commented Sep 13, 2025

Adds a new Grafana dashboard for Jira that is sprint-based rather than time-based. Some of the features:

Hides the global time picker
Introduces a sprint selection dropdown to scope all panels Does not modify the existing Kanban (time-based) dashboard

⚠️ Pre Checklist

Please complete ALL items in this checklist, and remove before submitting

  • I have read through the Contributing Documentation.
  • I have added relevant tests.
  • I have added relevant documentation.
  • I will add labels to the PR, such as pr-type/bug-fix, pr-type/feature-development, etc.

Summary

What does this PR do?
The PR adds a new Grafana dashboard for Jira. While the existing Jira dashboard is used for time based Kanban boards, this PR contributes an additional Jira board which is used for sprint boards. The board doesn't have time picker. Instead, I have added sprint dropdown. All tiles correspond to the sprint dropdown.

Screenshots

Sprint dropdown with 'Multiple selection' and 'All' option. Time picker is removed
image

Adds a new Grafana dashboard for Jira that is sprint-based rather than time-based. Some of the features:

Hides the global time picker
Introduces a sprint selection dropdown to scope all panels
Does not modify the existing Kanban (time-based) dashboard
@dosubot dosubot bot added size:XXL This PR changes 1000+ lines, ignoring generated files. component/ext This issue or PR relates to external components, such as Grafana pr-type/feature-development This PR is to develop a new feature labels Sep 13, 2025
@Bhoopalan1999 Bhoopalan1999 changed the title pr-type/feature-development: Jira Sprint Board (Grafana Dashboard) feat: Jira Sprint Board (Grafana Dashboard) Sep 13, 2025
@klesh klesh requested a review from Startrekzky September 15, 2025 04:10
@Startrekzky
Copy link
Copy Markdown
Contributor

Hi @Bhoopalan1999 , I checked the dashboard and it's overall good.

  1. My biggest question is why there is a cycle time when Issue lead time already exists? They're the same to me.

I checked the SQL and found you joined tables like _tool_jira_issue_changelogs and excluded specific issue types such as 'In Progress' and 'Developing'. This seems applicable only to your specific cases rather than a general approach. My suggestion is to remove this section if you want to get this merged to the main branch, which won't affect your ux, as you can still keep this section on your local dashboard.

image
  1. The number of overall issue delivery rate is not consistent with the rate by sprint
    I didn't dig into it. Please take a look.
image
  1. Please remove all status/type/... filters only applicable to your own use cases.
    Take 'delivery rate' as an example, I checked the SQL and found that you added the condition to exclude 'cancelled' status. This is not recommended if you want to share this with other users.
image

Copy link
Copy Markdown
Contributor

@Startrekzky Startrekzky left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left my comments under the PR

@Bhoopalan1999
Copy link
Copy Markdown
Author

Hello @Startrekzky

Thanks for the feedback.

1. Difference between Cycle Lead Time & Issue Lead Time:
Cycle time calculates how long has it taken for an issue to be resolved from the time it was moved to 'In Progress' or 'Developing'. That's why you see only these two Jira status selected in the sql query.
Issue lead time, on the other hand, calculates the entire lifetime of a specific Jira issue (from the time of the issue creation -> till it is marked as 'delivered'). Considering this is a sprint board, I thought having both would be useful for sprint board users as most of my teams who opted for sprint board needed Cycle time. I shall remove it and update you so that it is consistent with existing Jira Kanban board.

2. The number of overall issue delivery rate is not consistent with the rate by sprint
It's working in my dashboards. I'll troubleshoot and fix it and raise another PR.

3. Please remove all status/type/... filters only applicable to your own use cases.
Sure. I'll remove that and update in the next PR.

Fixed: 
1. My biggest question is why there is a cycle time when Issue lead time already exists? They're the same to me. - Removed them
2. The number of overall issue delivery rate is not consistent with the rate by sprint - The possible reason is that issue_type was a custom variable in my dashboard and it is a query variable in existing Jira dashboard. Fixed it. 
3. Please remove all status/type/... filters only applicable to your own use cases - Removed that in all tiles of the jira sprint dashboard
@Bhoopalan1999
Copy link
Copy Markdown
Author

I've made the changes in the new commit:
Fixed:

  1. My biggest question is why there is a cycle time when Issue lead time already exists? They're the same to me. - Removed them
  2. The number of overall issue delivery rate is not consistent with the rate by sprint - The possible reason is that issue_type was a custom variable in my dashboard and it is a query variable in existing Jira dashboard. Fixed it. Working fine in my board.
  3. Please remove all status/type/... filters only applicable to your own use cases - Removed that in all tiles of the jira sprint dashboard

@petkostas petkostas changed the title feat: Jira Sprint Board (Grafana Dashboard) feat(grafana): jira sprint board Oct 9, 2025
@petkostas petkostas changed the title feat(grafana): jira sprint board feat(grafana): jira sprint based dashboards Oct 9, 2025
Comment thread grafana/JiraSprintBoard.json Outdated
Comment thread grafana/dashboard/JiraSprintBoard.json Outdated
Comment thread grafana/dashboard/JiraSprintBoard.json
Comment thread grafana/JiraSprintBoard.json Outdated
@Bhoopalan1999
Copy link
Copy Markdown
Author

Hello @Startrekzky

Thanks for the feedback. I've updated all the changes requested. Also, I've now moved the file. Previously, I've accidentally kept the file inside grafana folder directly instead of grafana/dashboards. That's fixed now. Please review.

@klesh
Copy link
Copy Markdown
Contributor

klesh commented Feb 26, 2026

@Startrekzky Can you find time to review the PR? Thanks.

@klesh
Copy link
Copy Markdown
Contributor

klesh commented Feb 26, 2026

Please update the ASF header before we can merge—thanks!

Copy link
Copy Markdown
Contributor

@Startrekzky Startrekzky left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are the files under /backend/plugins updated?

@danielemoraschi
Copy link
Copy Markdown
Contributor

Hi,

Just recently came across this thread and wanted to share how I have been reporting Sprints in my instance.

A key difference in my approach is that I use issue_changelogs to reconstruct each issue's status and story points as they were at sprint end, rather than using i.status (the issue's current status). Without that, an issue completed after the sprint closed still shows as "delivered" for that sprint, which can skew delivery rates over time. I also track scope changes, issues added or removed mid-sprint, using the Sprint field changelog.

Full query below, if of any help.
You might have to adapt filters as needed.

WITH sprint_data AS (
    -- Get sprint info and explicitly filter to the current Board loop
    SELECT DISTINCT
        s.id AS sprint_id,
        s.name AS sprint_name,
        s.started_date,
        s.completed_date
    FROM sprints s
        JOIN boards b ON s.original_board_id = b.id
    WHERE 
        s.name REGEXP CONCAT('^', '${Sprints:regex}')
        AND $__timeFilter(s.started_date)
        -- This restricts the sprints to the board currently being rendered
        AND (b.name IN ("${Boards}") OR "${Boards}" = '')
),

sprint_issues_base AS (
	SELECT 
        j.id AS issue_id,
        j.issue_key,
        j.status AS current_status,
        j.story_point AS current_story_points,
        j.original_type,
        j.assignee_name,
        j.original_project,
        sd.sprint_id,
        sd.sprint_name,
        sd.started_date,
        sd.completed_date
    FROM issues j
    JOIN sprint_issues spi ON spi.issue_id = j.id
    JOIN sprint_data sd ON spi.sprint_id = sd.sprint_id
    LEFT JOIN board_issues boi ON boi.issue_id = j.id
    LEFT JOIN boards bo ON bo.id = boi.board_id
    WHERE j.original_type NOT IN ('Epic', 'Sub-task')
      AND (j.assignee_name IN ($Person) OR $Person = '')
      AND (bo.name IN ("${Boards}") OR "${Boards}" = '')
      AND j.original_project = "${JiraProjects}"
),

added_to_sprint AS (
    SELECT 
        sib.issue_id,
        sib.sprint_id,
        sib.issue_key,
        sib.current_status,
        sib.current_story_points,     
        TIMESTAMP(sib.started_date) as time,
        sib.sprint_name, 
        sib.started_date,
        sib.completed_date,
        -- Find the first changelog entry where this sprint was added to the issue
        (SELECT MIN(icl.created_date)
         FROM issue_changelogs icl 
         WHERE icl.issue_id = sib.issue_id
           AND icl.field_name = 'Sprint'
           AND icl.original_to_value LIKE CONCAT('%', sib.sprint_id, '%')
           -- And the sprint was NOT in the "from" value (i.e., it was actually added, not just moved)
           AND (icl.original_from_value NOT LIKE CONCAT('%', sib.sprint_id, '%') OR icl.original_from_value IS NULL)
        ) AS added_date,
        NULL as removed_date
    FROM sprint_issues_base sib
),

removed_from_sprint AS (
	SELECT 
        j.id AS issue_id,
        sd.sprint_id,
        j.issue_key,
        j.status AS current_status,
        j.story_point AS current_story_points,
        TIMESTAMP(sd.started_date) as time,
        sd.sprint_name,
        sd.started_date,
        sd.completed_date,
        NULL as added_date,
        icl.created_date AS removed_date
    FROM issues j
    JOIN issue_changelogs icl ON icl.issue_id = j.id
    JOIN sprint_data sd ON 1=1
    LEFT JOIN board_issues boi ON boi.issue_id = j.id
    LEFT JOIN boards bo ON bo.id = boi.board_id
    WHERE 
        -- Sprint field change
        icl.field_name = 'Sprint'
        -- Was in this sprint (from value contains sprint ID)
        AND (icl.original_from_value LIKE CONCAT('%', sd.sprint_id, '%') OR icl.original_to_value IS NULL)
        -- Was removed from this sprint (to value does NOT contain sprint ID or is null)
        AND (icl.original_to_value NOT LIKE CONCAT('%', sd.sprint_id, '%') OR icl.original_to_value IS NULL)
        -- Removal happened during the sprint window
        AND icl.created_date >= sd.started_date 
        AND (icl.created_date <= sd.completed_date OR sd.completed_date IS NULL)
        -- Filters
        AND j.original_type NOT IN ('Epic', 'Sub-task')
        AND (bo.name IN ("${Boards}") OR "${Boards}" = '')
        AND j.original_project = "${JiraProjects}"
        -- Ensure issue is NOT currently in the sprint (wasn't re-added)
        AND NOT EXISTS (
        	SELECT 1 FROM sprint_issues spi 
           	WHERE spi.issue_id = j.id AND spi.sprint_id = sd.sprint_id
        )
),

all_sprint_issues AS (
	SELECT 
		asp.* FROM added_to_sprint asp
	UNION ALL
		SELECT 
			rsp.* FROM removed_from_sprint rsp
),

status_at_sprint_end AS (
    SELECT 
	asi.*,
    	
        -- Get the most recent status BEFORE or AT the reference date
        (SELECT icl.to_value 
         FROM issue_changelogs icl 
         WHERE icl.issue_id = asi.issue_id 
           AND icl.field_name = 'status'
           AND icl.created_date <= asi.completed_date
         ORDER BY icl.created_date DESC 
         LIMIT 1
        ) AS last_status_before_sprint,
        
        -- Get the FIRST status change ever (to determine initial state)
        (SELECT icl.from_value 
         FROM issue_changelogs icl 
         WHERE icl.issue_id = asi.issue_id 
           AND icl.field_name = 'status'
         ORDER BY icl.created_date ASC 
         LIMIT 1
        ) AS initial_status
    
    FROM all_sprint_issues asi
),

status_at_sprint_end_final AS (
	SELECT 
		sase.*,
		COALESCE(last_status_before_sprint, initial_status, "TODO") as status_in_sprint
	FROM status_at_sprint_end sase
),

story_points_at_sprint_end AS (
    SELECT 
        asi.*,
        
        COALESCE(
            -- If there was a change AFTER reference date, get the "from" value of the earliest change
            (SELECT CAST(icl.original_to_value AS DECIMAL(10,1))
             FROM issue_changelogs icl 
             WHERE icl.issue_id = asi.issue_id 
               AND icl.field_name = 'Story Points'
               AND icl.created_date <= asi.completed_date
             ORDER BY icl.created_date DESC 
             LIMIT 1),
             
            (SELECT 
                CASE 
                    WHEN icl.original_from_value IS NULL OR TRIM(icl.original_from_value) = '' 
                    THEN 0  -- Story points were NULL at sprint end (ticket had no points initially)
                    ELSE CAST(icl.original_from_value AS DECIMAL(10,1))  -- Use the from_value as initial
                END
             FROM issue_changelogs icl 
             WHERE icl.issue_id = asi.issue_id 
               AND icl.field_name = 'Story Points'
             ORDER BY icl.created_date ASC 
             LIMIT 1),
             
            asi.current_story_points
        ) AS historical_story_points
        
    FROM status_at_sprint_end_final asi
)


SELECT * FROM story_points_at_sprint_end ORDER BY started_date ASC

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

component/ext This issue or PR relates to external components, such as Grafana pr-type/feature-development This PR is to develop a new feature size:XXL This PR changes 1000+ lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants