monte 0.1.0 → 0.2.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fe28c6cadcd331c236ef5dd53e43b9d32d18b9585fbcc7debc820e1abcb28394
4
- data.tar.gz: 998b21dbf4105649811833eef488e9ad858876759f9bb974f5805937655bacad
3
+ metadata.gz: ecddb357e33227ebed1a3149df0a58bc61b0762a76c895a10d329cb27c504460
4
+ data.tar.gz: 0163ead370cdc96747a039961fcb5a8835cf93998bf3737d05ca0f8d409b6fe1
5
5
  SHA512:
6
- metadata.gz: 26eb9d0115fad3fc93ecf4dd3d007d35573da5e0468ca6fa04c91fedc85499a79edda481a7af08a1be643e0a14229ce5b5a23d8e5388d61e652f63ea55b177b8
7
- data.tar.gz: 8c1bc63cefcde03c48e2d2217415b20469ed9937d75c428b9ad701c12bff625ff59040025ae2954597f6fe3e7ad434980a39180c4cbcd3cdcdff3c4d97c01f16
6
+ metadata.gz: '0298468a2c201f869396178d9429bab012a9fc2f873912079c07d0952af3c66e435915b5d804efbae30855669cffcb6c15ace1430cc29b7f4b6eecc2dfd052db'
7
+ data.tar.gz: 1ed6f3bf20fe4a997493a453263372ecc46a4bdbad30db511423c71c32d26b3ea49567edd4f8eaf218522569c3dd95d53c269e01f4990068e6e8d44201642bfd
data/.gitignore CHANGED
@@ -9,5 +9,7 @@
9
9
  /log/
10
10
  /lib/github_cli/man/u
11
11
 
12
+ #built gem
13
+ monte-*
12
14
  # rspec failure tracking
13
15
  .rspec_status
@@ -1,6 +1,12 @@
1
1
  # Change log
2
2
 
3
- ## [v0.0.1] - 2020-11-08
3
+ ## [v0.2.0] - 2020-11-21
4
+
5
+ ### Added
6
+ * Updated the `carlo` command to ask for a JIRA data export that can be used to
7
+ build a distribution of historic throughput based on a JQL filter
8
+
9
+ ## [v0.1.0] - 2020-11-08
4
10
 
5
11
  ### Added
6
12
  * Added core command: `carlo`
data/README.md CHANGED
@@ -22,32 +22,63 @@ have little previous data with which to build your forecast.
22
22
 
23
23
  ```sh
24
24
  $ monte carlo
25
- >
26
- > __ __ _
27
- > | \/ | ___ _ __ | |_ ___
28
- > | |\/| | / _ \ | '_ \ | __| / _ \
29
- > | | | | | (_) | | | | | | |_ | __/
30
- > |_| |_| \___/ |_| |_| \__| \___|
31
- >
32
- > Please answer the following:
33
- >
34
- > How many items do you have in your backlog? 40
35
- > How certain are you with regard to the scope of the work? medium
36
- > When will you start work (e.g. 28/04/2021) 2020-11-15
37
- > What is the smallest number of tasks/tickets you have completed in a week? 2
38
- > What is the largest number of tasks/tickets you have completed in a week? 6
39
- > How many simulations would you like to run 10000
40
- >
41
- >
42
- > Your Results
43
- >
44
- > ┌──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┐
45
- > │ 5% │ 15% │ 30% │ 50% │ 70% │ 85% │ 95% │
46
- > ├──────────┼──────────┼──────────┼──────────┼──────────┼──────────┼──────────┤
47
- > │2021-02-14│2021-02-21│2021-02-28│2021-02-28│2021-03-07│2021-03-14│2021-03-21│
48
- > └──────────┴──────────┴──────────┴──────────┴──────────┴──────────┴──────────┘
25
+ | \/ | ___ _ __ | |_ ___
26
+ | |\/| | / _ \ | '_ \ | __| / _ \
27
+ | | | | | (_) | | | | | | |_ | __/
28
+ |_| |_| \___/ |_| |_| \__| \___|
29
+
30
+ Welcome to Monte, a tool to help you answer the question: 'When will the work be done?'
31
+
32
+
33
+ Please answer the following questions
34
+ How many tasks/tickets do you have left to complete? 40
35
+ Do you have a JIRA csv export to use? no
36
+ Enter the smallest number of tasks/tickets you have finished in a week? 2
37
+ Enter the largest number of tasks/tickets you have finished in a week? 6
38
+ When will you start work (e.g. 28/04/2021) 2020-11-21
39
+ How certain are you with regard to the scope of the work? high
40
+ How many simulations would you like to run? 10000
41
+ ┌──────────────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┐
42
+ │Forecast Certainty│ 5% │ 15% │ 30% │ 50% │ 70% │ 85% │ 95% │
43
+ ├──────────────────┼──────────┼──────────┼──────────┼──────────┼──────────┼──────────┼──────────┤
44
+ │ Forecast Date │2021-02-06│2021-02-06│2021-02-13│2021-02-13│2021-02-20│2021-02-27│2021-03-06│
45
+ └──────────────────┴──────────┴──────────┴──────────┴──────────┴──────────┴──────────┴──────────┘
49
46
  ```
50
47
 
48
+ Forecasting like this works better when you use real data from the past to model
49
+ what the future will look like. To enable this Monte can use a JIRA export of
50
+ tasks completed over whatever historic timeframe you would like. Once you have
51
+ created your JQL filter ensure that you include the "Resolved" column and then
52
+ export as a csv. When running the app you will be given the option to specify
53
+ the path of the file and this will then build a distribution of previous
54
+ throughputs for use in the simulations. The following is a runthrough using a
55
+ test data file.
56
+
57
+ ```sh
58
+ __ __ _
59
+ | \/ | ___ _ __ | |_ ___
60
+ | |\/| | / _ \ | '_ \ | __| / _ \
61
+ | | | | | (_) | | | | | | |_ | __/
62
+ |_| |_| \___/ |_| |_| \__| \___|
63
+
64
+ Welcome to Monte, a tool to help you answer the question: 'When will the work be done?'
65
+
66
+
67
+ Please answer the following questions
68
+ How many tasks/tickets do you have left to complete? 40
69
+ Do you have a JIRA csv export to use? Yes
70
+ what is the absolute file path to the csv file /Users/user/directory/monte/spec/test_data/data.csv
71
+ When will you start work (e.g. 28/04/2021) 2020-11-21
72
+ How certain are you with regard to the scope of the work? medium
73
+ How many simulations would you like to run? 10000
74
+ ┌──────────────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┐
75
+ │Forecast Certainty│ 5% │ 15% │ 30% │ 50% │ 70% │ 85% │ 95% │
76
+ ├──────────────────┼──────────┼──────────┼──────────┼──────────┼──────────┼──────────┼──────────┤
77
+ │ Forecast Date │2021-02-06│2021-02-13│2021-02-20│2021-03-06│2021-03-13│2021-03-27│2021-04-10│
78
+ └──────────────────┴──────────┴──────────┴──────────┴──────────┴──────────┴──────────┴──────────┘
79
+ ```
80
+
81
+
51
82
  ## Development
52
83
 
53
84
  After checking out the repo, run `bin/setup` to install dependencies. Then, run
@@ -26,7 +26,7 @@ module Monte
26
26
  invoke :help, ['carlo']
27
27
  else
28
28
  require_relative 'commands/carlo'
29
- Monte::Commands::Carlo.new(options).execute
29
+ Monte::Commands::Carlo.new.execute
30
30
  end
31
31
  end
32
32
  end
@@ -3,52 +3,84 @@
3
3
  require 'date'
4
4
  require_relative '../command'
5
5
  require_relative '../simulation'
6
+ require_relative '../csv_data'
6
7
 
7
8
  module Monte
8
9
  module Commands
9
10
  # Runs Monte Carlo Simulation to estimate how long a piece of work will take
10
11
  class Carlo < Monte::Command
11
12
  include Simulation
12
- CERTAINTY = { 'low' => 1.8, 'medium' => 1.5, 'high' => 1.2 }.freeze
13
+ include CSVData
14
+ BLURB = %(Welcome to Monte, a tool to help you answer the question: 'When will the work be done?'\n\n
15
+ Please answer the following questions\n)
16
+ CERTAINTY = { 'certain' => 1.0, 'high' => 1.2, 'medium' => 1.5, 'low' => 1.8 }.freeze
13
17
  RUNS = { '10000' => 10_000, '1000' => 1000, '500' => 500 }.freeze
14
- HEADERS = ['5%', '15%', '30%', '50%', '70%', '85%', '95%'].freeze
18
+ HEADERS = ['Forecast Certainty', '5%', '15%', '30%', '50%', '70%', '85%', '95%'].freeze
15
19
  PERCENTILES = [0.05, 0.15, 0.3, 0.5, 0.7, 0.85, 0.95].freeze
16
20
 
17
- def initialize(options)
18
- @options = options
19
- end
20
-
21
- def execute(output: $stdout)
22
- output.puts(create_header)
23
- output.puts("Please answer the following:\n\n")
24
- user_input = ask_questions!
21
+ def execute
22
+ puts(create_header, BLURB)
23
+ user_input = ask_questions({})
25
24
  results = percentiles(user_input)
26
- output.puts("\n\nYour Results\n\n")
27
- output.puts(create_table(results))
25
+ puts(create_table(results))
28
26
  end
29
27
 
30
28
  def create_table(rows)
31
- table(HEADERS, [rows]).render(:unicode, alignment: [:center])
29
+ table(HEADERS, [rows.prepend('Forecast Date')])
30
+ .render(:unicode, alignment: [:center])
32
31
  end
33
32
 
34
33
  def create_header
35
34
  large_title('Monte')
36
35
  end
37
36
 
38
- def ask_questions!
39
- prompt.collect do
40
- key(:backlog).ask('How many items do you have in your backlog?', convert: :int)
41
- key(:split_factor).select('How certain are you with regard to the scope of the work?', CERTAINTY)
42
- key(:start_date).ask('When will you start work (e.g. 28/04/2021)') do |q|
43
- q.default Date.today
44
- q.convert ->(input) { Date.parse(input.to_s) }
45
- end
46
- key(:low).ask('What is the smallest number of tasks/tickets you have completed in a week?', convert: :int)
47
- key(:high).ask('What is the largest number of tasks/tickets you have completed in a week?', convert: :int)
48
- key(:runs).select('How many simulations would you like to run', RUNS)
37
+ def ask_questions(options)
38
+ q1 = ask_backlog(options)
39
+ q2 = ask_throughput(q1)
40
+ q3 = ask_start(q2)
41
+ q4 = ask_split(q3)
42
+ ask_runs(q4)
43
+ end
44
+
45
+ def ask_backlog(options)
46
+ options.merge(backlog:
47
+ prompt.ask('How many tasks/tickets do you have left to complete?',
48
+ required: true,
49
+ convert: :int))
50
+ end
51
+
52
+ def ask_split(options)
53
+ options.merge(split:
54
+ prompt.select('How certain are you with regard to the scope of the work?', CERTAINTY))
55
+ end
56
+
57
+ def ask_start(options)
58
+ options.merge(start:
59
+ prompt.ask('When will you start work (e.g. 28/04/2021)') do |q|
60
+ q.required true
61
+ q.default Date.today
62
+ q.convert ->(input) { Date.parse(input.to_s) }
63
+ end)
64
+ end
65
+
66
+ def ask_throughput(options)
67
+ data_exists = prompt.yes?('Do you have a JIRA csv export to use?', required: true)
68
+ if data_exists
69
+ path = prompt.ask('what is the absolute file path to the csv file', required: true)
70
+ throughput = historic_throughput(path)
71
+ else
72
+ low = prompt.ask('Enter the smallest number of tasks/tickets you have finished in a week?', convert: :int)
73
+ high = prompt.ask('Enter the largest number of tasks/tickets you have finished in a week?', convert: :int)
74
+ throughput = (low..high).to_a
49
75
  end
76
+ options.merge(throughput: throughput)
50
77
  end
51
78
 
79
+ def ask_runs(options)
80
+ options.merge(
81
+ runs: prompt.select('How many simulations would you like to run?', RUNS)
82
+ )
83
+ end
52
84
  end
53
85
  end
54
86
  end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'csv'
4
+ require 'date'
5
+
6
+ # This module supports parsing an export of Jira data to build a sample of
7
+ # historic throughputs that provide a more accurate forecast
8
+ # It takes the absolute path to the csv export and returns an array containing
9
+ # the number of tasks/tickets completed each week
10
+ module CSVData
11
+ RESOLVED = 'Resolved'
12
+ def historic_throughput(path)
13
+ headers, *data = CSV.read(path)
14
+ resolved_column = headers.index(RESOLVED)
15
+ resolved_dates = data.flat_map { |row| Date.parse row[resolved_column] }
16
+ grouped_into_weeks = resolved_dates.group_by { |date| date - date.wday }
17
+ grouped_into_weeks.values.map(&:length)
18
+ end
19
+ end
@@ -10,32 +10,30 @@
10
10
  module Simulation
11
11
  PERCENTILES = [0.05, 0.15, 0.3, 0.5, 0.7, 0.85, 0.95].freeze
12
12
 
13
- def percentiles(args)
14
- results = run_simulations(args).sort
13
+ def percentiles(options)
14
+ results = run_simulations(options).sort
15
15
  PERCENTILES.map do |percentile|
16
- index = args[:runs] * (percentile - 1)
16
+ index = options[:runs] * (percentile - 1)
17
17
  results[index]
18
18
  end
19
19
  end
20
20
 
21
- def run_simulations(args)
22
- estimated_backlog = args[:backlog] * args[:split_factor]
23
- Array.new(args[:runs]) do |_|
24
- args[:start_date] + simulate(
21
+ def run_simulations(options)
22
+ estimated_backlog = options[:backlog] * options[:split]
23
+ Array.new(options[:runs]) do |_|
24
+ options[:start] + simulate(
25
25
  estimated_backlog,
26
- args[:low],
27
- args[:high]
26
+ options[:throughput]
28
27
  ) * 7
29
28
  end
30
29
  end
31
30
 
32
- def simulate(backlog, low, high, result = 0)
31
+ def simulate(backlog, throughput, result = 0)
33
32
  return result if backlog <= 0
34
33
 
35
34
  simulate(
36
- backlog - rand(low..high),
37
- low,
38
- high,
35
+ backlog - throughput.sample,
36
+ throughput,
39
37
  result + 1
40
38
  )
41
39
  end
@@ -1,3 +1,3 @@
1
1
  module Monte
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: monte
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Werner
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-11-15 00:00:00.000000000 Z
11
+ date: 2020-11-21 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: |-
14
14
  If you are an engineer who is being asked, 'When will
@@ -37,6 +37,7 @@ files:
37
37
  - lib/monte/command.rb
38
38
  - lib/monte/commands/.gitkeep
39
39
  - lib/monte/commands/carlo.rb
40
+ - lib/monte/csv_data.rb
40
41
  - lib/monte/simulation.rb
41
42
  - lib/monte/version.rb
42
43
  - monte.gemspec