It's not the same without you

Join the community to find out what other Atlassian users are discussing, debating and creating.

Atlassian Community Hero Image Collage

Burndown charts without Greenhopper

Hi, we are evaluating JIRA for issue tracking. We are only exploring basic Agile concepts and cannot justify purchasing the full Greenhopper plugin on top of JIRA at this stage.

It seems that in Standard JIRA it should be possible to generate a rudimentary burndown chart for a given Version (aka iteration) using the existing Time tracking fields.

I have seen mention of the Laughing Panda plugin from 2008 but it seems that plugin is no longer available or does not work well any more.

Are there any alternatives to generate basic burndown charts in Standard JIRA?

Thank you :)

10 answers

1 accepted

5 votes
Answer accepted

You can use eazyBI reporting application for JIRA to create burndown charts for selected project version, issue types and display either open issues or aggregated estimate for open issues each day.

Here is example open issues burndown chart that I created based on Confluence project version 4.0.5 from jira.atlassian.com. It displays just number of open issues but also aggregated time estimates or story points could be displayed (they were not available in this example project). Though just original estimates could be used as JIRA REST API (which is used by eazyBI) does not provide change history of remaining time estimates.

If you are interested then try out eazyBI and ask support for help to define measures that you would like to track in burndown chart.

There is a solution which we used to by defining a jndi connection to the Jira database, and using that Jndi connection, plot the burn down charts in Confluence using the combination of sql macro, chart macro and beanshell macro. Of course it requires that you have a Confluence instance and also know a bit about how to write the sql and java.

A crude solution, but it works :)

Sample Code

{composition-setup}
{run:replace=StartDate:29-Sep-2008,EndDate:24-Oct-2008,Group:<<removed>>, SprintId:1,FilterId:11560|autoRun=true|titleRun=Redraw|hideRun=true|hideParameters=true|titleRun=Plot}

h3. $SprintId Backlog 
{sql:jndi=jira}
select JiraKey, Summary, ifnull(Assignee,'Unassigned') Assignee, Status, OrginalEstimate, RemainingEstimate from 
(select jiradb3121.jiraissue.pkey JiraKey, jiradb3121.jiraissue.summary Summary,
jiradb3121.propertystring.propertyvalue Assignee,jiradb3121.issuestatus.pname Status,
TRUNCATE(jiradb3121.jiraissue.timeoriginalestimate/(60*60),0) OrginalEstimate,
TRUNCATE(jiradb3121.jiraissue.timeestimate/(60*60),0) RemainingEstimate,
TRUNCATE(jiradb3121.jiraissue.timespent/(60*60),0) TimeSpent
from jiradb3121.jiraissue
left outer join jiradb3121.userbase on jiradb3121.jiraissue.assignee = jiradb3121.userbase.username
left outer join jiradb3121.propertyentry on jiradb3121.userbase.id = jiradb3121.propertyentry.ENTITY_ID
left outer join jiradb3121.propertystring on jiradb3121.propertyentry.id = jiradb3121.propertystring.id 
left outer join jiradb3121.customfieldvalue on jiradb3121.customfieldvalue.issue = jiradb3121.jiraissue.id
left outer join jiradb3121.issuestatus on jiradb3121.issuestatus.sequence = jiradb3121.jiraissue.issuestatus
where
(jiradb3121.propertyentry.ENTITY_NAME = 'OSUser' OR jiradb3121.propertyentry.ENTITY_NAME IS NULL) and
(jiradb3121.propertyentry.PROPERTY_KEY = 'fullName'  OR jiradb3121.propertyentry.PROPERTY_KEY IS NULL) and
jiradb3121.customfieldvalue.stringvalue = $SprintId and
jiradb3121.customfieldvalue.customfield = 10447 and
jiradb3121.jiraissue.project = 10220 and
jiradb3121.jiraissue.reporter in (select username from userbase,groupbase,membershipbase where membershipbase.user_name= userbase.username and membershipbase.group_name='$Group' and
groupbase.groupname = membershipbase.group_name) limit 100 ) data
{sql}

*For details {color:red}(everything related to sprint items in this scrum){color}, follow the link [<<removed>> $SprintId Data|<<removed>>/secure/IssueNavigator.jspa?mode=hide&requestId=$FilterId]*


h3. Efforts Burndown chart
{section}
{column:width=60%}
{beanshell:output=wiki}
import java.util.Date;
import java.text.*;
import java.util.*;

String logs = "";
void log(String msg)
{
	logs += msg + "\n";
}

String formatdata(int i)
{
	if(i<10)
	{
		return "0"+i;
	}
	else
	{
		return ""+i;
	}
}

String getFormatedDate(Date d)
{
	return (d.getYear()+1900) + "-" + formatdata(d.getMonth()+1) + "-" + formatdata(d.getDate());
}

Date reduceDays(Date d, long days)
{
	long time = d.getTime();
	time = time - (1000*24*60*60)*days;
	Date tempD = new Date();
	tempD.setTime(time);
	return tempD;
}

Date incrementDays(Date d, long days)
{
	long time = d.getTime();
	time = time + (1000*24*60*60)*days;
	Date tempD = new Date();
	tempD.setTime(time);
	return tempD;
}

out.println("{chart:displayData=true|type=line|title=Burndown Chart for Sprint $SprintId |yLabel=Remaining Hours|xLabel=Days|timeSeries=true|width=500|height=500|");
out.println("|dataDisplay=false|dataOrientation=vertical}");
out.println("||Day|| Remaining Estimate|| Ideal ||");

SimpleDateFormat df1 = new SimpleDateFormat( "dd-MMM-yyyy" );
Date d = df1.parse( "29-Oct-2008" );

Date startDate = df1.parse("$StartDate");
Date endDate = df1.parse("$EndDate");

int numberOfDays = (endDate.getTime() - startDate.getTime())/(1000*60*60*24);

for (int countofdays=0;countofdays<=numberOfDays;countofdays++)
{
	out.print("|" + formatdata(startDate.getDate()) + " | ");
	out.println(" {sql:jndi=jira|table=false}");
	out.println(" SELECT SUM(ESTIMATE) FROM ( SELECT ifnull(cast(VT1.NEWSTRING as decimal),");
	out.println(" jiradb3121.JIRAISSUE.TIMEORIGINALESTIMATE)/(60*60) ESTIMATE from jiradb3121.jiraissue LEFT OUTER JOIN ");
	out.println(" ( SELECT CG.ISSUEID ISSUEID, CG.CREATED CREATED, jiradb3121.CHANGEITEM.NEWSTRING NEWSTRING FROM ");
	out.println(" jiradb3121.CHANGEGROUP CG, jiradb3121.CHANGEITEM WHERE CG.ID = jiradb3121.CHANGEITEM.GROUPID AND ");
	out.println(" CG.ID IN (SELECT CG1.ID FROM jiradb3121.CHANGEGROUP CG1, (SELECT jiradb3121.CHANGEGROUP.ISSUEID ISSUEID,");
	out.println(" MAX(jiradb3121.CHANGEGROUP.CREATED) CREATED FROM jiradb3121.CHANGEGROUP, jiradb3121.JIRAISSUE, ");
	out.println(" jiradb3121.CUSTOMFIELDVALUE, jiradb3121.CHANGEITEM WHERE jiradb3121.CHANGEGROUP.CREATED <= '" + getFormatedDate(startDate) + " 23:59:59.0' ");
	out.println(" AND jiradb3121.CHANGEGROUP.ISSUEID = jiradb3121.JIRAISSUE.ID AND jiradb3121.JIRAISSUE.PROJECT=10220 ");
	out.println(" AND jiradb3121.JIRAISSUE.REPORTER IN (select username from userbase,groupbase,membershipbase where membershipbase.user_name= userbase.username and membershipbase.group_name='$Group' and groupbase.groupname = membershipbase.group_name) ");
	out.println(" AND jiradb3121.CUSTOMFIELDVALUE.CUSTOMFIELD='10447' AND jiradb3121.CUSTOMFIELDVALUE.ISSUE=jiradb3121.JIRAISSUE.ID ");
	out.println(" AND jiradb3121.CUSTOMFIELDVALUE.STRINGVALUE=$SprintId AND jiradb3121.CUSTOMFIELDVALUE.ISSUE=jiradb3121.JIRAISSUE.ID AND ");
	out.println(" jiradb3121.CHANGEITEM.GROUPID = jiradb3121.CHANGEGROUP.ID AND jiradb3121.CHANGEITEM.field = 'timeestimate' ");
	out.println(" GROUP BY jiradb3121.CHANGEGROUP.ISSUEID)IVT WHERE IVT.ISSUEID = CG1.ISSUEID AND IVT.CREATED = CG1.CREATED) ");


	out.println(" AND jiradb3121.CHANGEITEM.field = 'timeestimate') VT1 ON JIRAISSUE.ID = VT1.ISSUEID, jiradb3121.CUSTOMFIELDVALUE ");
	out.println(" WHERE jiradb3121.JIRAISSUE.PROJECT=10220 AND jiradb3121.CUSTOMFIELDVALUE.CUSTOMFIELD='10447' ");
	out.println(" AND jiradb3121.CUSTOMFIELDVALUE.ISSUE=jiradb3121.JIRAISSUE.ID and jiradb3121.CUSTOMFIELDVALUE.STRINGVALUE=$SprintId ");
	out.println(" AND jiradb3121.JIRAISSUE.REPORTER IN (select username from userbase,groupbase,membershipbase where membershipbase.user_name= userbase.username and membershipbase.group_name='$Group' and groupbase.groupname = membershipbase.group_name) ");
	out.println(" AND jiradb3121.CUSTOMFIELDVALUE.ISSUE=jiradb3121.JIRAISSUE.ID) EST");
	out.print("{sql} ");

	out.println("| {sql:jndi=jira|table=false}");
	out.println(" SELECT (SUM(jiradb3121.JIRAISSUE.TIMEORIGINALESTIMATE) - (SUM(jiradb3121.JIRAISSUE.TIMEORIGINALESTIMATE)*" +(countofdays)+ "/"+numberOfDays+"))/(60*60) FROM jiradb3121.JIRAISSUE, jiradb3121.CUSTOMFIELDVALUE WHERE");
	out.println(" jiradb3121.JIRAISSUE.PROJECT=10220 AND ");
	out.println(" jiradb3121.CUSTOMFIELDVALUE.CUSTOMFIELD='10447' AND ");
	out.println(" jiradb3121.CUSTOMFIELDVALUE.STRINGVALUE=$SprintId AND ");
	out.println(" jiradb3121.CUSTOMFIELDVALUE.ISSUE=jiradb3121.JIRAISSUE.ID AND ");
	out.println(" jiradb3121.JIRAISSUE.REPORTER IN (select username from userbase,groupbase,membershipbase where membershipbase.user_name= userbase.username and membershipbase.group_name='$Group' and groupbase.groupname = membershipbase.group_name) ");
	out.println("{sql} |");
	
	startDate = incrementDays(startDate , 1);

}
out.println("{chart}");

{beanshell}
{column}

{column:width=40%}
{roundrect:title=Status}
{jiraportlet:url=<<removed>>/secure/RunPortlet.jspa?portletKey=com.atlassian.jira.plugin.system.portlets:filterstats&filterid=$FilterId&statistictype=statuses&}
{roundrect}
{roundrect:title=Assignee}
{jiraportlet:url=<<removed>>/secure/RunPortlet.jspa?portletKey=com.atlassian.jira.plugin.system.portlets:filterstats&filterid=$FilterId&statistictype=assignees&}
{roundrect}
{column}
{section}
{run}

Why not use a web solution for this such as http://www.burndown-charts.com/

Unless youn really want it in Jira, maybe to mix them in with your Jira issues.

Why not use a web solution for this such as http://www.burndown-charts.com/

Unless youn really want it in Jira

You can render custom burn down charts also with the JIRA PDF View Plugin easily:

  1. Run the standard filter "issues in this version" from the UI, and invoke the plugin from the "View" dropdown menu.
  2. Write a little Groovy script that iterates the returned collection of issues, and:
    1. Computes the sum of the "original estimate" field. This is the starting point for the burndown chart (total efforts estimated for this version).
    2. Computes the "logged time" for each closed issue. Then builds a map (dates to long values): put the day of closing (key) and logged time (value). If there is already a value with that key, then add the two and replace the old entry in the map. At the end, this map will contain the "time burnt down per day" values.
    3. Generates a JFreeChart timeseries dataset out of the previously calculated total and daily burn down.
  3. Create a simple PDF template that displays a single timeseries chart, which takes the dataset returned by the previous script to render a timeseries chart. Check the timeseries chart example in the tutorial for directions.

This is fairly straightforward to implement, and you have full control in how to visualize your data.

In version 2.0.0 of the beforementioned JIRA PDF View Plugin, you can find a ready-made Burn Down Chart template.

Not only it displays the issue burn down in a give scope, it also displays some other key agile metrics:

  1. Remaining days and days gone
  2. Done issues and outstanding issues
  3. Velocity (number of issues resolved and created per day)
  4. Ideal velocity
  5. Actual velocity

It is fully template driven, so it is customizable to any potential requirements.

Sample (as PDF):

... why not just export to Excel and create burnup/down from there? Better control over charting there, easy, and cheap...

It's a bit saddening that answers so far have been for plugins, but that should indicate to you the state of the built-in JIRA Agile tracking. It does not function, for me, in the case of a custom workflow.

Definitely cannot recommend JIRA if a good built-in burndown is something you want. If you use the basic workflow, though, it'll be good enough.

Suggest an answer

Log in or Sign up to answer
This widget could not be displayed.
This widget could not be displayed.
Community showcase
Published Apr 09, 2019 in Portfolio for Jira

Portfolio for Jira 3.0 is here!

The wait is over... Portfolio for Jira Server and Data Center 3.0 is now officially here! Platform releases offer Atlassian an opportunity to shift our strategy, make bold predictions about t...

1,454 views 15 26
Read article

Atlassian User Groups

Connect with like-minded Atlassian users at free events near you!

Find a group

Connect with like-minded Atlassian users at free events near you!

Find my local user group

Unfortunately there are no AUG chapters near you at the moment.

Start an AUG

You're one step closer to meeting fellow Atlassian users at your local meet up. Learn more about AUGs

Groups near you