jirametrics 2.0.1 → 2.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/jirametrics/aging_work_bar_chart.rb +17 -9
- data/lib/jirametrics/aging_work_in_progress_chart.rb +8 -6
- data/lib/jirametrics/aging_work_table.rb +8 -15
- data/lib/jirametrics/anonymizer.rb +6 -5
- data/lib/jirametrics/chart_base.rb +19 -17
- data/lib/jirametrics/css_variable.rb +33 -0
- data/lib/jirametrics/cycletime_scatterplot.rb +9 -7
- data/lib/jirametrics/daily_wip_by_age_chart.rb +9 -9
- data/lib/jirametrics/daily_wip_by_blocked_stalled_chart.rb +22 -14
- data/lib/jirametrics/daily_wip_chart.rb +2 -2
- data/lib/jirametrics/dependency_chart.rb +18 -14
- data/lib/jirametrics/downloader.rb +6 -7
- data/lib/jirametrics/examples/aggregated_project.rb +3 -1
- data/lib/jirametrics/examples/standard_project.rb +10 -4
- data/lib/jirametrics/expedited_chart.rb +5 -2
- data/lib/jirametrics/exporter.rb +26 -20
- data/lib/jirametrics/grouping_rules.rb +7 -1
- data/lib/jirametrics/html/aging_work_bar_chart.erb +12 -6
- data/lib/jirametrics/html/aging_work_in_progress_chart.erb +8 -2
- data/lib/jirametrics/html/aging_work_table.erb +1 -1
- data/lib/jirametrics/html/cycletime_histogram.erb +9 -3
- data/lib/jirametrics/html/cycletime_scatterplot.erb +10 -4
- data/lib/jirametrics/html/daily_wip_chart.erb +10 -5
- data/lib/jirametrics/html/expedited_chart.erb +11 -5
- data/lib/jirametrics/html/hierarchy_table.erb +1 -1
- data/lib/jirametrics/html/index.css +174 -0
- data/lib/jirametrics/html/index.erb +8 -36
- data/lib/jirametrics/html/sprint_burndown.erb +11 -6
- data/lib/jirametrics/html/story_point_accuracy_chart.erb +9 -4
- data/lib/jirametrics/html/throughput_chart.erb +11 -5
- data/lib/jirametrics/html_report_config.rb +17 -2
- data/lib/jirametrics/issue.rb +3 -2
- data/lib/jirametrics/jira_gateway.rb +1 -1
- data/lib/jirametrics/project_config.rb +13 -18
- data/lib/jirametrics/settings.json +7 -0
- data/lib/jirametrics/sprint_burndown.rb +2 -2
- data/lib/jirametrics/status_collection.rb +1 -1
- data/lib/jirametrics/story_point_accuracy_chart.rb +8 -4
- data/lib/jirametrics/throughput_chart.rb +4 -1
- data/lib/jirametrics.rb +1 -0
- metadata +6 -3
| @@ -1,5 +1,5 @@ | |
| 1 1 |  | 
| 2 | 
            -
            <div>
         | 
| 2 | 
            +
            <div class="chart">
         | 
| 3 3 | 
             
              <canvas id="<%= chart_id %>" width="<%= canvas_width %>" height="<%= canvas_height %>"></canvas>
         | 
| 4 4 | 
             
            </div>
         | 
| 5 5 | 
             
            <script>
         | 
| @@ -23,7 +23,10 @@ new Chart(document.getElementById('<%= chart_id %>').getContext('2d'), { | |
| 23 23 | 
             
                    scaleLabel: {
         | 
| 24 24 | 
             
                      display: true,
         | 
| 25 25 | 
             
                      labelString: 'Date Completed'
         | 
| 26 | 
            -
                    }
         | 
| 26 | 
            +
                    },
         | 
| 27 | 
            +
                    grid: {
         | 
| 28 | 
            +
                      color: <%= CssVariable['--grid-line-color'].to_json %>
         | 
| 29 | 
            +
                    },
         | 
| 27 30 | 
             
                  },
         | 
| 28 31 | 
             
                  y: {
         | 
| 29 32 | 
             
                    scaleLabel: {
         | 
| @@ -33,7 +36,10 @@ new Chart(document.getElementById('<%= chart_id %>').getContext('2d'), { | |
| 33 36 | 
             
                      display: true,
         | 
| 34 37 | 
             
                      text: 'Count of items'
         | 
| 35 38 | 
             
                    },
         | 
| 36 | 
            -
                    min: 0
         | 
| 39 | 
            +
                    min: 0,
         | 
| 40 | 
            +
                    grid: {
         | 
| 41 | 
            +
                      color: <%= CssVariable['--grid-line-color'].to_json %>
         | 
| 42 | 
            +
                    },
         | 
| 37 43 | 
             
                  },
         | 
| 38 44 | 
             
                },
         | 
| 39 45 | 
             
                plugins: {
         | 
| @@ -52,8 +58,8 @@ new Chart(document.getElementById('<%= chart_id %>').getContext('2d'), { | |
| 52 58 | 
             
                        type: 'box',
         | 
| 53 59 | 
             
                        xMin: '<%= range.begin %>T00:00:00',
         | 
| 54 60 | 
             
                        xMax: '<%= range.end %>T23:59:59',
         | 
| 55 | 
            -
                        backgroundColor: ' | 
| 56 | 
            -
                        borderColor: ' | 
| 61 | 
            +
                        backgroundColor: <%= CssVariable.new('--non-working-days-color').to_json %>,
         | 
| 62 | 
            +
                        borderColor: <%= CssVariable.new('--non-working-days-color').to_json %>
         | 
| 57 63 | 
             
                      },
         | 
| 58 64 | 
             
                      <% end %>
         | 
| 59 65 | 
             
                    }
         | 
| @@ -36,11 +36,21 @@ class HtmlReportConfig | |
| 36 36 |  | 
| 37 37 | 
             
                File.open @file_config.output_filename, 'w' do |file|
         | 
| 38 38 | 
             
                  html_directory = "#{Pathname.new(File.realpath(__FILE__)).dirname}/html"
         | 
| 39 | 
            -
                   | 
| 39 | 
            +
                  css = load_css html_directory: html_directory
         | 
| 40 | 
            +
                  erb = ERB.new File.read(File.join(html_directory, 'index.erb'))
         | 
| 40 41 | 
             
                  file.puts erb.result(binding)
         | 
| 41 42 | 
             
                end
         | 
| 42 43 | 
             
              end
         | 
| 43 44 |  | 
| 45 | 
            +
              def load_css html_directory:
         | 
| 46 | 
            +
                base_css = File.read(File.join(html_directory, 'index.css'))
         | 
| 47 | 
            +
                extra_css_filename = settings['include_css']
         | 
| 48 | 
            +
                return base_css unless extra_css_filename && File.exist?(extra_css_filename)
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                @file_config.project_config.exporter.file_system.log("including css from file: #{extra_css_filename}")
         | 
| 51 | 
            +
                base_css << "\n\n" << File.read(extra_css_filename)
         | 
| 52 | 
            +
              end
         | 
| 53 | 
            +
             | 
| 44 54 | 
             
              def board_id id = nil
         | 
| 45 55 | 
             
                @board_id = id unless id.nil?
         | 
| 46 56 | 
             
                @board_id
         | 
| @@ -145,13 +155,18 @@ class HtmlReportConfig | |
| 145 155 | 
             
                execute_chart DependencyChart.new block
         | 
| 146 156 | 
             
              end
         | 
| 147 157 |  | 
| 158 | 
            +
              # have an explicit method here so that index.erb can call 'settings' just as any other erb can.
         | 
| 159 | 
            +
              def settings
         | 
| 160 | 
            +
                @file_config.project_config.settings
         | 
| 161 | 
            +
              end
         | 
| 162 | 
            +
             | 
| 148 163 | 
             
              def execute_chart chart, &after_init_block
         | 
| 149 164 | 
             
                project_config = @file_config.project_config
         | 
| 150 165 |  | 
| 151 166 | 
             
                chart.issues = issues
         | 
| 152 167 | 
             
                chart.time_range = project_config.time_range
         | 
| 153 168 | 
             
                chart.timezone_offset = timezone_offset
         | 
| 154 | 
            -
                chart.settings =  | 
| 169 | 
            +
                chart.settings = settings
         | 
| 155 170 |  | 
| 156 171 | 
             
                chart.all_boards = project_config.all_boards
         | 
| 157 172 | 
             
                chart.board_id = find_board_id if chart.respond_to? :board_id=
         | 
    
        data/lib/jirametrics/issue.rb
    CHANGED
    
    | @@ -30,7 +30,8 @@ class Issue | |
| 30 30 | 
             
                  fabricate_change(field_name: 'status'),
         | 
| 31 31 | 
             
                  fabricate_change(field_name: 'priority')
         | 
| 32 32 | 
             
                ].compact + @changes
         | 
| 33 | 
            -
              rescue  | 
| 33 | 
            +
              rescue # rubocop:disable Style/RescueStandardError
         | 
| 34 | 
            +
                # All we're doing is adding information to the existing exception and letting it propogate up
         | 
| 34 35 | 
             
                raise "Unable to initialize #{raw['key']}"
         | 
| 35 36 | 
             
              end
         | 
| 36 37 |  | 
| @@ -252,7 +253,7 @@ class Issue | |
| 252 253 | 
             
                end
         | 
| 253 254 |  | 
| 254 255 | 
             
                blocked_link_texts = settings['blocked_link_text']
         | 
| 255 | 
            -
                stalled_threshold = settings[' | 
| 256 | 
            +
                stalled_threshold = settings['stalled_threshold_days']
         | 
| 256 257 |  | 
| 257 258 | 
             
                blocking_issue_keys = []
         | 
| 258 259 |  | 
| @@ -29,7 +29,7 @@ class JiraGateway | |
| 29 29 |  | 
| 30 30 | 
             
              def load_jira_config jira_config
         | 
| 31 31 | 
             
                @jira_url = jira_config['url']
         | 
| 32 | 
            -
                raise  | 
| 32 | 
            +
                raise 'Must specify URL in config' if @jira_url.nil?
         | 
| 33 33 |  | 
| 34 34 | 
             
                @jira_email = jira_config['email']
         | 
| 35 35 | 
             
                @jira_api_token = jira_config['api_token']
         | 
| @@ -8,7 +8,7 @@ class ProjectConfig | |
| 8 8 |  | 
| 9 9 | 
             
              attr_reader :target_path, :jira_config, :all_boards, :possible_statuses,
         | 
| 10 10 | 
             
                :download_config, :file_configs, :exporter, :data_version, :name, :board_configs,
         | 
| 11 | 
            -
                :settings | 
| 11 | 
            +
                :settings
         | 
| 12 12 | 
             
              attr_accessor :time_range, :jira_url, :id
         | 
| 13 13 |  | 
| 14 14 | 
             
              def initialize exporter:, jira_config:, block:, target_path: '.', name: '', id: nil
         | 
| @@ -22,22 +22,12 @@ class ProjectConfig | |
| 22 22 | 
             
                @name = name
         | 
| 23 23 | 
             
                @board_configs = []
         | 
| 24 24 | 
             
                @all_boards = {}
         | 
| 25 | 
            -
                @settings =  | 
| 26 | 
            -
                  'stalled_threshold' => 5,
         | 
| 27 | 
            -
                  'blocked_statuses' => [],
         | 
| 28 | 
            -
                  'stalled_statuses' => [],
         | 
| 29 | 
            -
                  'blocked_link_text' => [],
         | 
| 30 | 
            -
             | 
| 31 | 
            -
                  'colors' => {
         | 
| 32 | 
            -
                    'stalled' => 'orange',
         | 
| 33 | 
            -
                    'blocked' => '#FF7400'
         | 
| 34 | 
            -
                  }
         | 
| 35 | 
            -
                }
         | 
| 25 | 
            +
                @settings = load_settings
         | 
| 36 26 | 
             
                @id = id
         | 
| 37 27 | 
             
              end
         | 
| 38 28 |  | 
| 39 29 | 
             
              def evaluate_next_level
         | 
| 40 | 
            -
                instance_eval(&@block)
         | 
| 30 | 
            +
                instance_eval(&@block) if @block
         | 
| 41 31 | 
             
              end
         | 
| 42 32 |  | 
| 43 33 | 
             
              def run
         | 
| @@ -58,6 +48,10 @@ class ProjectConfig | |
| 58 48 | 
             
                end
         | 
| 59 49 | 
             
              end
         | 
| 60 50 |  | 
| 51 | 
            +
              def load_settings
         | 
| 52 | 
            +
                JSON.parse(File.read(File.join(__dir__, 'settings.json')))
         | 
| 53 | 
            +
              end
         | 
| 54 | 
            +
             | 
| 61 55 | 
             
              def guess_project_id
         | 
| 62 56 | 
             
                return @id if @id
         | 
| 63 57 |  | 
| @@ -243,7 +237,7 @@ class ProjectConfig | |
| 243 237 |  | 
| 244 238 | 
             
                start = json['date_start'] || json['time_start'] # date_start is the current format. Time is the old.
         | 
| 245 239 | 
             
                stop  = json['date_end'] || json['time_end']
         | 
| 246 | 
            -
                @time_range = to_time(start)..to_time(stop)
         | 
| 240 | 
            +
                @time_range = to_time(start)..to_time(stop, end_of_day: true)
         | 
| 247 241 |  | 
| 248 242 | 
             
                @jira_url = json['jira_url']
         | 
| 249 243 | 
             
              rescue Errno::ENOENT
         | 
| @@ -251,8 +245,9 @@ class ProjectConfig | |
| 251 245 | 
             
                raise
         | 
| 252 246 | 
             
              end
         | 
| 253 247 |  | 
| 254 | 
            -
              def to_time string
         | 
| 255 | 
            -
                 | 
| 248 | 
            +
              def to_time string, end_of_day: false
         | 
| 249 | 
            +
                time = end_of_day ? '23:59:59' : '00:00:00'
         | 
| 250 | 
            +
                string = "#{string}T#{time}#{@timezone_offset}" if string.match?(/^\d{4}-\d{2}-\d{2}$/)
         | 
| 256 251 | 
             
                Time.parse string
         | 
| 257 252 | 
             
              end
         | 
| 258 253 |  | 
| @@ -436,8 +431,8 @@ class ProjectConfig | |
| 436 431 | 
             
                  else
         | 
| 437 432 | 
             
                    message << "days of data from #{issue.changes.first.time.to_date} to #{cutoff_time.to_date}"
         | 
| 438 433 | 
             
                  end
         | 
| 439 | 
            -
                   | 
| 434 | 
            +
                  exporter.file_system.log message
         | 
| 440 435 | 
             
                end
         | 
| 441 | 
            -
                 | 
| 436 | 
            +
                exporter.file_system.log "Discarded data from #{issues_cutoff_times.count} issues out of a total #{issues.size}"
         | 
| 442 437 | 
             
              end
         | 
| 443 438 | 
             
            end
         | 
| @@ -57,7 +57,7 @@ class SprintBurndown < ChartBase | |
| 57 57 | 
             
                result = +''
         | 
| 58 58 | 
             
                result << '<h1>Sprint Burndowns</h1>'
         | 
| 59 59 |  | 
| 60 | 
            -
                possible_colours =  | 
| 60 | 
            +
                possible_colours = (1..5).collect { |i| CssVariable["--sprint-burndown-sprint-color-#{i}"] }
         | 
| 61 61 | 
             
                charts_to_generate = []
         | 
| 62 62 | 
             
                charts_to_generate << [:data_set_by_story_points, 'Story Points'] if @use_story_points
         | 
| 63 63 | 
             
                charts_to_generate << [:data_set_by_story_counts, 'Story Count'] if @use_story_counts
         | 
| @@ -65,7 +65,7 @@ class SprintBurndown < ChartBase | |
| 65 65 | 
             
                  @summary_stats.clear
         | 
| 66 66 | 
             
                  data_sets = []
         | 
| 67 67 | 
             
                  sprints.each_with_index do |sprint, index|
         | 
| 68 | 
            -
                    color = possible_colours[index %  | 
| 68 | 
            +
                    color = possible_colours[index % possible_colours.size]
         | 
| 69 69 | 
             
                    label = sprint.name
         | 
| 70 70 | 
             
                    data = send(data_method, sprint: sprint, change_data_for_sprint: change_data_by_sprint[sprint])
         | 
| 71 71 | 
             
                    data_sets << {
         | 
| @@ -32,7 +32,7 @@ class StatusCollection | |
| 32 32 | 
             
                      next
         | 
| 33 33 | 
             
                    else
         | 
| 34 34 | 
             
                      all_status_names = @list.collect { |s| "#{s.name.inspect}:#{s.id.inspect}" }.uniq.sort.join(', ')
         | 
| 35 | 
            -
                      raise "Status not found: #{name_or_id}. Possible statuses are: #{all_status_names}"
         | 
| 35 | 
            +
                      raise "Status not found: \"#{name_or_id}\". Possible statuses are: #{all_status_names}"
         | 
| 36 36 | 
             
                    end
         | 
| 37 37 | 
             
                  end
         | 
| 38 38 |  | 
| @@ -11,7 +11,8 @@ class StoryPointAccuracyChart < ChartBase | |
| 11 11 | 
             
                    estimates can change over time, we're graphing the estimate at the time that the story started.
         | 
| 12 12 | 
             
                  </p>
         | 
| 13 13 | 
             
                  <p>
         | 
| 14 | 
            -
                    The completed dots indicate cycletimes. The aging dots ( | 
| 14 | 
            +
                    The completed dots indicate cycletimes. The aging dots (click on the legend to turn them on)
         | 
| 15 | 
            +
                    show the current
         | 
| 15 16 | 
             
                    age of items, which will give you a hint as to where they might end up. If they're already
         | 
| 16 17 | 
             
                    far to the right then you know you have a problem.
         | 
| 17 18 | 
             
                  </p>
         | 
| @@ -56,9 +57,12 @@ class StoryPointAccuracyChart < ChartBase | |
| 56 57 | 
             
                end
         | 
| 57 58 |  | 
| 58 59 | 
             
                [
         | 
| 59 | 
            -
                  [completed_hash, 'Completed', ' | 
| 60 | 
            -
                  [aging_hash, 'Still in progress', ' | 
| 61 | 
            -
                ].filter_map do |hash, label,  | 
| 60 | 
            +
                  [completed_hash, 'Completed', 'completed', false],
         | 
| 61 | 
            +
                  [aging_hash, 'Still in progress', 'active', true]
         | 
| 62 | 
            +
                ].filter_map do |hash, label, completed_or_active, starts_hidden|
         | 
| 63 | 
            +
                  fill_color = CssVariable["--estimate-accuracy-chart-#{completed_or_active}-fill-color"]
         | 
| 64 | 
            +
                  border_color = CssVariable["--estimate-accuracy-chart-#{completed_or_active}-border-color"]
         | 
| 65 | 
            +
             | 
| 62 66 | 
             
                  # We sort so that the smaller circles are in front of the bigger circles.
         | 
| 63 67 | 
             
                  data = hash.sort(&hash_sorter).collect do |key, values|
         | 
| 64 68 | 
             
                    estimate, cycle_time = *key
         | 
| @@ -25,7 +25,10 @@ class ThroughputChart < ChartBase | |
| 25 25 | 
             
                data_sets = []
         | 
| 26 26 | 
             
                if rules_to_issues.size > 1
         | 
| 27 27 | 
             
                  data_sets << weekly_throughput_dataset(
         | 
| 28 | 
            -
                    completed_issues: completed_issues, | 
| 28 | 
            +
                    completed_issues: completed_issues,
         | 
| 29 | 
            +
                    label: 'Totals',
         | 
| 30 | 
            +
                    color: CssVariable['--throughput_chart_total_line_color'],
         | 
| 31 | 
            +
                    dashed: true
         | 
| 29 32 | 
             
                  )
         | 
| 30 33 | 
             
                end
         | 
| 31 34 |  | 
    
        data/lib/jirametrics.rb
    CHANGED
    
    | @@ -51,6 +51,7 @@ class JiraMetrics < Thor | |
| 51 51 | 
             
                require 'jirametrics/daily_wip_chart'
         | 
| 52 52 | 
             
                require 'jirametrics/groupable_issue_chart'
         | 
| 53 53 | 
             
                require 'jirametrics/discard_changes_before'
         | 
| 54 | 
            +
                require 'jirametrics/css_variable'
         | 
| 54 55 |  | 
| 55 56 | 
             
                require 'jirametrics/aggregate_config'
         | 
| 56 57 | 
             
                require 'jirametrics/expedited_chart'
         | 
    
        metadata
    CHANGED
    
    | @@ -1,14 +1,14 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: jirametrics
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 2. | 
| 4 | 
            +
              version: 2.1.1
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Mike Bowler
         | 
| 8 8 | 
             
            autorequire:
         | 
| 9 9 | 
             
            bindir: bin
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date: 2024- | 
| 11 | 
            +
            date: 2024-05-08 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: random-word
         | 
| @@ -74,6 +74,7 @@ files: | |
| 74 74 | 
             
            - lib/jirametrics/change_item.rb
         | 
| 75 75 | 
             
            - lib/jirametrics/chart_base.rb
         | 
| 76 76 | 
             
            - lib/jirametrics/columns_config.rb
         | 
| 77 | 
            +
            - lib/jirametrics/css_variable.rb
         | 
| 77 78 | 
             
            - lib/jirametrics/cycletime_config.rb
         | 
| 78 79 | 
             
            - lib/jirametrics/cycletime_histogram.rb
         | 
| 79 80 | 
             
            - lib/jirametrics/cycletime_scatterplot.rb
         | 
| @@ -107,6 +108,7 @@ files: | |
| 107 108 | 
             
            - lib/jirametrics/html/data_quality_report.erb
         | 
| 108 109 | 
             
            - lib/jirametrics/html/expedited_chart.erb
         | 
| 109 110 | 
             
            - lib/jirametrics/html/hierarchy_table.erb
         | 
| 111 | 
            +
            - lib/jirametrics/html/index.css
         | 
| 110 112 | 
             
            - lib/jirametrics/html/index.erb
         | 
| 111 113 | 
             
            - lib/jirametrics/html/sprint_burndown.erb
         | 
| 112 114 | 
             
            - lib/jirametrics/html/story_point_accuracy_chart.erb
         | 
| @@ -118,6 +120,7 @@ files: | |
| 118 120 | 
             
            - lib/jirametrics/project_config.rb
         | 
| 119 121 | 
             
            - lib/jirametrics/rules.rb
         | 
| 120 122 | 
             
            - lib/jirametrics/self_or_issue_dispatcher.rb
         | 
| 123 | 
            +
            - lib/jirametrics/settings.json
         | 
| 121 124 | 
             
            - lib/jirametrics/sprint.rb
         | 
| 122 125 | 
             
            - lib/jirametrics/sprint_burndown.rb
         | 
| 123 126 | 
             
            - lib/jirametrics/sprint_issue_change_data.rb
         | 
| @@ -151,7 +154,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement | |
| 151 154 | 
             
                - !ruby/object:Gem::Version
         | 
| 152 155 | 
             
                  version: '0'
         | 
| 153 156 | 
             
            requirements: []
         | 
| 154 | 
            -
            rubygems_version: 3.5. | 
| 157 | 
            +
            rubygems_version: 3.5.10
         | 
| 155 158 | 
             
            signing_key:
         | 
| 156 159 | 
             
            specification_version: 4
         | 
| 157 160 | 
             
            summary: Extract Jira metrics
         |