monte 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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