Skip to content

Instantly share code, notes, and snippets.

@dragonai
Last active June 7, 2019 20:09
Show Gist options
  • Save dragonai/a1dae02d476f6055f82a to your computer and use it in GitHub Desktop.
Save dragonai/a1dae02d476f6055f82a to your computer and use it in GitHub Desktop.
Atlassian JIRA Sprint Progress Meter

##Preview

Description

Simple Dashing widget that displays a progress meter for your current JIRA sprint, with the completed and total point values at the bottom.

##Usage

#####Dependencies

Add jira-ruby to the gemfile:

gem 'jira-ruby'

and then just run

$ bundle install

#####Setup

To install this widget, simply run dashing install a1dae02d476f6055f82a.

Then substitute the following placeholders in sprint_progress.rb with the appropriate values:

  • ENV['JIRA_USERNAME'] => your JIRA username (optionally can be placed in your environment variables file
  • ENV['JIRA_PASSWORD'] => your JIRA password (also optionally can be placed in your environment variables file
  • https://your-jira-instance.atlassian.net => the location of your JIRA instance
  • SPRINT_WIDGET_DATA_ID => the target HTML element's data-id attribute in your layout

Finally, to include the widget on a dashboard, drop the following snippet into your layout:

<li data-row="1" data-col="1" data-sizex="1" data-sizey="1">
  <div data-view="SprintProgress" data-id="whatever_id_you_like"></div>
</li>
class Dashing.SprintProgress extends Dashing.Widget
@accessor 'value', Dashing.AnimatedValue
constructor: ->
super
@observe 'value', (value) ->
$(@node).find(".meter").val(value).trigger('change')
ready: ->
meter = $(@node).find(".meter")
meter.attr("data-bgcolor", meter.css("background-color"))
meter.attr("data-fgcolor", meter.css("color"))
meter.knob()
<h1 class="title" data-bind="title"></h1>
<input class="meter" data-angleOffset=-125 data-angleArc=250 data-width=200 data-readOnly=true data-bind-value="value | shortenedNumber | append '%'" data-bind-data-min="min" data-bind-data-max="max">
<p class="more-info" data-bind="moreinfo"></p>
<p class="updated-at" data-bind="updatedAtMessage"></p>
require 'jira'
SCHEDULER.every '5m', :first_in => 0 do |job|
client = JIRA::Client.new({
:username => ENV['JIRA_USERNAME'],
:password => ENV['JIRA_PASSWORD'],
:site => "https://your-jira-instance.atlassian.net",
:auth_type => :basic,
:context_path => ""
})
closed_points = client.Issue.jql("sprint in openSprints() and status = \"closed\"").map{ |issue| issue.fields['customfield_10004'] }.reduce(:+) || 0
total_points = client.Issue.jql("sprint in openSprints()").map{ |issue| issue.fields['customfield_10004'] }.reduce(:+) || 0
if total_points == 0
percentage = 0
moreinfo = "No sprint currently in progress"
else
percentage = ((closed_points/total_points)*100).to_i
moreinfo = "#{closed_points.to_i} / #{total_points.to_i}"
end
send_event('SPRINT_WIDGET_DATA_ID', { title: "Sprint Progress", min: 0, value: percentage, max: 100, moreinfo: moreinfo })
end
// ----------------------------------------------------------------------------
// Sass declarations
// ----------------------------------------------------------------------------
$background-color: steelblue;
$title-color: rgba(255, 255, 255, 0.7);
$moreinfo-color: rgba(255, 255, 255, 0.5);
$meter-background: darken($background-color, 7%);
// ----------------------------------------------------------------------------
// Widget-sprint-progress styles
// ----------------------------------------------------------------------------
.widget-sprint-progress {
background-color: $background-color;
input.meter {
background-color: $meter-background;
color: #fff;
}
.title {
color: $title-color;
}
.more-info {
color: $moreinfo-color;
}
.updated-at {
color: rgba(0, 0, 0, 0.3);
}
}
@mad666
Copy link

mad666 commented Jan 13, 2015

Hi, does this also work with the JIRA Agile? What do I have to enter in the context_path to get my current sprint?

@dragonai
Copy link
Author

I believe it should! context_path should be the specific suffix that completes the link that the JIRA client resolves to. Check this link out for more info, I think you can make it work with a little bit of playing around: https://confluence.atlassian.com/display/JIRAKB/How+to+change+the+JIRA+context+path

@gambastian
Copy link

Hi,

First thanks for the widget.
Could you help me with a problem? When creating the JIRA client in the sprint_progress.rb it throws a exception saying that the method has 1 parameter extra, do you know why this happens?

@gambastian
Copy link

Hi again, I solved the last issue (The problem was that I forfgot to update the gemfile).

But now I have another problemm, each time it tries to excecute de jql looking for the open sprints it throws an HTTPError. You know why could that be?

Thanks

@budgester
Copy link

Yup see something similar here...
scheduler caught exception:
JIRA::HTTPError
/home/martin.stevens/.rvm/gems/ruby-1.9.3-p551/gems/jira-ruby-0.1.14/lib/jira/request_client.rb:14:in request' /home/martin.stevens/.rvm/gems/ruby-1.9.3-p551/gems/jira-ruby-0.1.14/lib/jira/client.rb:159:inrequest'
/home/martin.stevens/.rvm/gems/ruby-1.9.3-p551/gems/jira-ruby-0.1.14/lib/jira/client.rb:138:in get' /home/martin.stevens/.rvm/gems/ruby-1.9.3-p551/gems/jira-ruby-0.1.14/lib/jira/resource/issue.rb:54:injql'
/home/martin.stevens/.rvm/gems/ruby-1.9.3-p551/gems/jira-ruby-0.1.14/lib/jira/base_factory.rb:33:in block (2 levels) in delegate_to_target_class' /home/martin.stevens/sweet_dashboard_project/jobs/sprint_progress.rb:12:inblock in <top (required)>'

@zmagajna
Copy link

Did anyone manage to resolve this issue(JIRA::HTTPError)?

@armiller
Copy link

I was having issues with JIRA::HTTPError so I wrapped the client.Issue.jql searches within a rescue block outputted the error from JIRA. That would look like this:

 begin
    closed_points = client.Issue.jql("sprint in openSprints() and status = \"closed\"").map{ |issue| issue.fields['customfield_10004'] }.reduce(:+) || 0
    total_points = client.Issue.jql("sprint in openSprints()").map{ |issue| issue.fields['customfield_10004'] }.reduce(:+) || 0
  rescue JIRA::HTTPError => error
    puts error.code + ' ' + error.message
    puts error.response
  end

It turned out that my JIRA endpoint was plain http and it was trying to connect using ssl for some reason. I added :use_ssl => nil to the JIRA client params and it fixed that connection error. You also may want to check for the :rest_api_path and see if it correct.

@igoratencompass
Copy link

Hi,
First thanks for the nice widget. I'm getting the following error though:

scheduler caught exception:
undefined method +' for nil:NilClass /home/ubuntu/dashing-encompass/jobs/sprint_progress.rb:14:ineach'
/home/ubuntu/dashing-encompass/jobs/sprint_progress.rb:14:in reduce' /home/ubuntu/dashing-encompass/jobs/sprint_progress.rb:14:inblock in <top (required)>'
/home/ubuntu/.rvm/gems/ruby-2.1.1/gems/rufus-scheduler-2.0.24/lib/rufus/sc/jobs.rb:230:in call' /home/ubuntu/.rvm/gems/ruby-2.1.1/gems/rufus-scheduler-2.0.24/lib/rufus/sc/jobs.rb:230:intrigger_block'
/home/ubuntu/.rvm/gems/ruby-2.1.1/gems/rufus-scheduler-2.0.24/lib/rufus/sc/jobs.rb:204:in block in trigger' /home/ubuntu/.rvm/gems/ruby-2.1.1/gems/rufus-scheduler-2.0.24/lib/rufus/sc/scheduler.rb:430:incall'
/home/ubuntu/.rvm/gems/ruby-2.1.1/gems/rufus-scheduler-2.0.24/lib/rufus/sc/scheduler.rb:430:in `block in trigger_job'

Any idea?
Thanks,

@shaimr
Copy link

shaimr commented Jul 25, 2015

@igoratencompass - it means you don't use customfield_10004 (I assume it represent the story points).

@MuchRandoms
Copy link

Hi, I'm curious, has anyone been able to get this widget working? I've followed the instructions above and when I start dashing what is retrieved from the JIRA instance is the entire HTML page for the sprint which obviously the widget can't work with. Can anyone share a scrubbed version of the context_path parameter they got to work?

@dragonai on your closed_points and total_points calculations you are using the JIRA openSprints() function. We have had the experience here that openSprints will retrieve items from the current sprint AND future planned sprints that have not started. We got around that by using the futureSprints() function. SO "sprint in openSprints() and not in futureSprints()". As stated I haven't been able to get the widget running yet so I can't confirm if that issue is occurring.

@stevehughes
Copy link

@igoratencompass - Did you manage to fix your issue as i'm having the same error?

@biancalpadilla
Copy link

@igoratencompass @stevehughes having same issue.

I checked my JIRA and am using customfield_10004 as Story Points @shaimr, it was working at one point but now stopped.

Did anyone figure it out? Could really use some help!

@robertolos
Copy link

I finally fixed the JIRA::HTTPError. Instead of using your email use the username that can be found in your Jira profile

@lhtdesignde
Copy link

lhtdesignde commented Jun 1, 2016

could anyone fix the issue with uncaught exception with undefined method? i adjusted the customfield to the one we use for story points. but seems to still not work. any idea why this is happening?

line 24 is:
percentage = ((closed_points/total_points)*100).to_i

scheduler caught exception: undefined method/' for nil:NilClass
jobs/sprint_progress.rb:24:in block in <top (required)>' .rbenv/versions/2.2.4/lib/ruby/gems/2.2.0/gems/rufus-scheduler-2.0.24/lib/rufus/sc/jobs.rb:230:incall'
.rbenv/versions/2.2.4/lib/ruby/gems/2.2.0/gems/rufus-scheduler-2.0.24/lib/rufus/sc/jobs.rb:230:in trigger_block' .rbenv/versions/2.2.4/lib/ruby/gems/2.2.0/gems/rufus-scheduler-2.0.24/lib/rufus/sc/jobs.rb:204:inblock in trigger'
.rbenv/versions/2.2.4/lib/ruby/gems/2.2.0/gems/rufus-scheduler-2.0.24/lib/rufus/sc/scheduler.rb:430:in call' .rbenv/versions/2.2.4/lib/ruby/gems/2.2.0/gems/rufus-scheduler-2.0.24/lib/rufus/sc/scheduler.rb:430:inblock in trigger_job'`

@lhtdesignde
Copy link

after loging this better i'm actually getting a 401 Unauthorized.

@Nachiket26
Copy link

I am getting this error :

bundler: failed to load command: thin (/usr/local/bin/thin)
SyntaxError: /var/lib/gems/1.9.1/gems/jira-ruby-1.4.0/lib/jira/resource/project.rb:30:
syntax error, unexpected tLABEL, expecting ')'
def users(start_at: nil, max_results: nil)
^
/var/lib/gems/1.9.1/gems/jira-ruby-1.4.0/lib/jira/resource/project.rb:30:
Can't assign to nil
def users(start_at: nil, max_results: nil)
^
/var/lib/gems/1.9.1/gems/jira-ruby-1.4.0/lib/jira/resource/project.rb:43:
syntax error, unexpected keyword_end, expecting $end
/var/lib/gems/1.9.1/gems/backports-3.8.0/lib/backports/std_lib.rb:9:in
require' /var/lib/gems/1.9.1/gems/backports-3.8.0/lib/backports/std_lib.rb:9:in require_with_backports'
/var/lib/gems/1.9.1/gems/jira-ruby-1.4.0/lib/jira-ruby.rb:21:in
<top (required)>' /var/lib/gems/1.9.1/gems/backports-3.8.0/lib/backports/std_lib.rb:9:in require'
/var/lib/gems/1.9.1/gems/backports-3.8.0/lib/backports/std_lib.rb:9:in
require_with_backports' /home/bpmuser/test/jobs/sprint_progress.rb:1:in <top (required)>'
/var/lib/gems/1.9.1/gems/backports-3.8.0/lib/backports/std_lib.rb:9:in
require' /var/lib/gems/1.9.1/gems/backports-3.8.0/lib/backports/std_lib.rb:9:in require_with_backports'
/var/lib/gems/1.9.1/gems/smashing-1.0.0/lib/dashing/app.rb:171:in
block in require_glob' /var/lib/gems/1.9.1/gems/smashing-1.0.0/lib/dashing/app.rb:170:in each'
/var/lib/gems/1.9.1/gems/smashing-1.0.0/lib/dashing/app.rb:170:in
require_glob' /var/lib/gems/1.9.1/gems/smashing-1.0.0/lib/dashing/app.rb:181:in <top (required)>'
/var/lib/gems/1.9.1/gems/smashing-1.0.0/lib/dashing.rb:3:in require' /var/lib/gems/1.9.1/gems/smashing-1.0.0/lib/dashing.rb:3:in <top (required)>'
config.ru:1:in require' config.ru:1:in block in

'
/var/lib/gems/1.9.1/gems/rack-1.5.5/lib/rack/builder.rb:55:in instance_eval' /var/lib/gems/1.9.1/gems/rack-1.5.5/lib/rack/builder.rb:55:in initialize'
config.ru:1:in new' config.ru:1:in '
/var/lib/gems/1.9.1/gems/thin-1.6.4/lib/rack/adapter/loader.rb:33:in eval' /var/lib/gems/1.9.1/gems/thin-1.6.4/lib/rack/adapter/loader.rb:33:in load'
/var/lib/gems/1.9.1/gems/thin-1.6.4/lib/thin/controllers/controller.rb:182:in
load_rackup_config' /var/lib/gems/1.9.1/gems/thin-1.6.4/lib/thin/controllers/controller.rb:72:in start'
/var/lib/gems/1.9.1/gems/thin-1.6.4/lib/thin/runner.rb:200:in run_command' /var/lib/gems/1.9.1/gems/thin-1.6.4/lib/thin/runner.rb:156:in run!'
/var/lib/gems/1.9.1/gems/thin-1.6.4/bin/thin:6:in <top (required)>' /usr/local/bin/thin:23:in load'
/usr/local/bin/thin:23:in `<top (required)>'

I am using the smashing plugin for Jenkins status collection and on top of it I installed sprint progress.

Could you please help me on this?

@Pritesh-Patel
Copy link

@Nachiket26 the job file needs to be changed to use require 'jira-ruby' instead of require 'jira'

@azakhary
Copy link

Dose this still work? We are using current story points modern version which does not actually have customfield_10004

@azakhary
Copy link

closed_points = client.Issue.jql("sprint in openSprints() and status = \"resolved\" AND sprint not in futureSprints() AND \"Story Points\">0").map{ |issue| issue.fields['customfield_10012'] }.reduce(:+) || 0 total_points = client.Issue.jql("sprint in openSprints() AND sprint not in futureSprints() AND \"Story Points\">0").map{ |issue| issue.fields['customfield_10012'] }.reduce(:+) || 0

This seems to be working now if anyone needs it

@lhtdesignde
Copy link

i finally got it working with the latest jira version. for me it was the 2 factor auth that kept giving me the 401. after disabling that, it connected.

@phdd
Copy link

phdd commented Mar 11, 2019

The following worked for me

  closed_points = client.Issue.jql("sprint in openSprints() and status in (\"Done\", \"Obsolet\")")
    .map { |issue| issue.fields['customfield_10025'] }
    .select { |points| points != nil }
    .reduce(:+) || 0

  total_points = client.Issue.jql("sprint in openSprints()")
    .map { |issue| issue.fields['customfield_10025'] }
    .select { |points| points != nil }
    .reduce(:+) || 0

@musicbyjing
Copy link

Anyone know a way to query all issues for a specific board with the JIRA API?

@SergioGomez321
Copy link

SergioGomez321 commented Jun 7, 2019

I m getting JIRA::HTTPERROR that is a 401 error, Why is this happening? how can i solve it? help me please!
@lhtdesignde what did you do? :(

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