Class: CycletimeScatterplot

Inherits:
TimeBasedScatterplot show all
Defined in:
lib/jirametrics/cycletime_scatterplot.rb

Constant Summary collapse

PROXY_FOR_MOST_PERCENTILE =

The number that the "reasonable proxy for most" claim is actually about. That claim is only defensible near this value: at the median half the work runs longer, and at the 98th you are describing the worst case, not the typical one. So the sentence appears when this percentile is on the chart and is silently dropped when it is not, rather than being reworded into something that sounds authoritative and is wrong.

85

Constants inherited from TimeBasedScatterplot

TimeBasedScatterplot::OVERALL_LABEL

Constants inherited from TimeBasedChart

TimeBasedChart::VALUE_AXIS_LABELS

Constants inherited from ChartBase

ChartBase::LABEL_POSITIONS, ChartBase::OKABE_ITO_PALETTE

Instance Attribute Summary collapse

Attributes inherited from TimeBasedScatterplot

#percentage_lines, #y_axis_cap_percentile

Attributes included from GroupableIssueChart

#group_by_block, #issue_hints, #issue_periods

Attributes inherited from ChartBase

#aggregated_project, #all_boards, #atlassian_document_format, #board_id, #canvas_height, #canvas_width, #color_palette, #data_quality, #date_range, #file_system, #fix_versions, #holiday_dates, #issues, #settings, #time_range, #timezone_offset, #x_axis_title, #y_axis_title

Instance Method Summary collapse

Methods inherited from TimeBasedScatterplot

#cap_y_axis, #compute_cap, #create_datasets, #data_for_item, #legend_annotation_map, #percentile_label, #percentile_lines_for, #percentile_value, #percentiles, #run, #show_trend_lines, #trend_line_data_set, #trend_line_description, #value_axis_title=

Methods included from PercentileValidation

validate_percentile, validate_percentiles

Methods included from GroupableIssueChart

#accumulate_issue_for_group, #group_issues, #grouping_rules, #init_configuration_block, #reconcile_percentiles

Methods inherited from TimeBasedChart

#duration_in_unit, #label_cycletime

Methods inherited from ChartBase

#aggregated_project?, #before_run, #call_before_run, #canvas, #canvas_responsive?, #chart_format, #collapsible_issues_panel, #color_block, #color_for, #comma_and, #completed_issues_in_range, #current_board, #cycletime, #cycletime_for_issue, #daily_chart_dataset, #date_annotation, #describe_non_working_days, #description_text, #format_integer, #format_status, #header_text, #holidays, #html_directory, #icon_span, #link_to_issue, #next_id, #next_palette_color, #non_working_day?, #normalize_annotation_datetime, #not_visible_icon, #not_visible_text, #ordinal, #percentile_of, #render, #render_axis_title, #render_top_text, #resolve_status, #stagger_label_positions, #status_category_color, #to_human_readable, #working_days_annotation, #wrap_and_render

Constructor Details

#initialize(block) ⇒ CycletimeScatterplot

Returns a new instance of CycletimeScatterplot.



8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# File 'lib/jirametrics/cycletime_scatterplot.rb', line 8

def initialize block
  super()

  header_text 'Cycletime Scatterplot'
  description_text <<-HTML
    <div class="p">
      This chart shows only completed work and indicates both what day it completed as well as
      how many days it took to get done. Hovering over a dot will show you the ID of the work item.
    </div>
    <%= percentile_description %>
    <%= trend_line_description %>
    #{describe_non_working_days}
  HTML
  @x_axis_title = 'Date completed'
  @y_axis_title = 'Cycletime in days'

  init_configuration_block block do
    grouping_rules do |issue, rule|
      rule.label = issue.type
      rule.color = color_for type: issue.type
    end
  end
end

Instance Attribute Details

#possible_statusesObject

Returns the value of attribute possible_statuses.



6
7
8
# File 'lib/jirametrics/cycletime_scatterplot.rb', line 6

def possible_statuses
  @possible_statuses
end

Instance Method Details

#all_itemsObject



43
44
45
# File 'lib/jirametrics/cycletime_scatterplot.rb', line 43

def all_items
  completed_issues_in_range include_unstarted: false
end

#cycletime_unit(unit) ⇒ Object

Days-only until the working-days cycletime engine grows sub-day resolution. Inherited from TimeBasedChart so the option exists, but the unbuilt path fails loudly rather than mislabelling the axis while the value stays in days. See jirametrics-en5.

Raises:

  • (NotImplementedError)


35
36
37
# File 'lib/jirametrics/cycletime_scatterplot.rb', line 35

def cycletime_unit unit
  raise NotImplementedError, "#{self.class} only supports :days for now (see jirametrics-en5)" unless unit == :days
end

#minimum_y_valueObject



39
40
41
# File 'lib/jirametrics/cycletime_scatterplot.rb', line 39

def minimum_y_value
  1 # Values under 1 day are data quality problems; they're flagged in the quality report instead
end

#per_type_sentence(lines) ⇒ Object



119
120
121
122
123
124
125
126
# File 'lib/jirametrics/cycletime_scatterplot.rb', line 119

def per_type_sentence lines
  if lines.size == 1
    "The other lines reflect the #{lines.first[:percentile]}% line for that respective type of work."
  else
    'Each type of work also gets its own lines in its own colour, at whichever percentiles ' \
      'were configured for that type. Hover any line to see which one it is and its value.'
  end
end

#percentile_descriptionObject

The prose follows the configuration, so it has to read well for one percentile or several, and say nothing at all for none. Values come from percentage_lines because run has already computed them, and because this string is not run through ERB a second time.



73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
# File 'lib/jirametrics/cycletime_scatterplot.rb', line 73

def percentile_description
  overall_lines = percentage_lines.select { |line| line[:dataset_index].nil? }
  return '' if overall_lines.empty?

  sentences = [
    percentile_summary_sentence(overall_lines),
    proxy_for_most_sentence(overall_lines),
    per_type_sentence(overall_lines)
  ].compact
  <<-HTML
    <div class="p">
      #{sentences.join ' '}
    </div>
  HTML
end

#percentile_summary_sentence(lines) ⇒ Object

Mechanical and true whatever the configured percentiles are. Singular phrasing is preserved word for word from the original so the default chart reads exactly as it always has.



91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/jirametrics/cycletime_scatterplot.rb', line 91

def percentile_summary_sentence lines
  swatch = color_block '--cycletime-scatterplot-overall-trendline-color'
  if lines.size == 1
    percentile = lines.first[:percentile]
    "The #{swatch} line indicates the #{ordinal percentile} percentile " \
      "(#{lines.first[:value]} days). #{percentile}% of all items on this chart fall on or " \
      "below the line and the remaining #{100 - percentile}% are above the line."
  else
    # No values inline here. With several lines the sentence turns into a wall of parentheses,
    # and each value is already on the line's hover label and in that group's legend entry.
    described = lines.collect { |line| ordinal line[:percentile] }
    "The #{swatch} lines indicate the #{comma_and described} percentiles of all items on " \
      'this chart. For each line, that percentage of items fall on or below it and the rest ' \
      'are above.'
  end
end

#proxy_for_most_sentence(lines) ⇒ Object

Only emitted when 85 is actually among the configured percentiles. See PROXY_FOR_MOST_PERCENTILE.



109
110
111
112
113
114
115
116
117
# File 'lib/jirametrics/cycletime_scatterplot.rb', line 109

def proxy_for_most_sentence lines
  line = lines.find { |candidate| candidate[:percentile] == PROXY_FOR_MOST_PERCENTILE }
  return nil unless line

  "#{PROXY_FOR_MOST_PERCENTILE}% is a " \
    '<a href="https://jirametrics.org/faq/#why-85">reasonable proxy</a> for "most" so that we ' \
    'can say that based on this data set, we can predict that most work of this type will ' \
    "complete in #{line[:value]} days or less."
end

#title_value(item, rules: nil) ⇒ Object



55
56
57
58
# File 'lib/jirametrics/cycletime_scatterplot.rb', line 55

def title_value item, rules: nil
  hint = @issue_hints&.fetch(item, nil)
  "#{item.key} : #{item.summary} (#{label_days(y_value(item))})#{" #{hint}" if hint}"
end

#x_value(item) ⇒ Object



47
48
49
# File 'lib/jirametrics/cycletime_scatterplot.rb', line 47

def x_value item
  item.started_stopped_times.last
end

#y_value(item) ⇒ Object



51
52
53
# File 'lib/jirametrics/cycletime_scatterplot.rb', line 51

def y_value item
  item.board.cycletime.cycletime(item)
end