monte 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: fe28c6cadcd331c236ef5dd53e43b9d32d18b9585fbcc7debc820e1abcb28394
4
+ data.tar.gz: 998b21dbf4105649811833eef488e9ad858876759f9bb974f5805937655bacad
5
+ SHA512:
6
+ metadata.gz: 26eb9d0115fad3fc93ecf4dd3d007d35573da5e0468ca6fa04c91fedc85499a79edda481a7af08a1be643e0a14229ce5b5a23d8e5388d61e652f63ea55b177b8
7
+ data.tar.gz: 8c1bc63cefcde03c48e2d2217415b20469ed9937d75c428b9ad701c12bff625ff59040025ae2954597f6fe3e7ad434980a39180c4cbcd3cdcdff3c4d97c01f16
@@ -0,0 +1,13 @@
1
+ /.yardoc
2
+ /.bundle
3
+ /coverage/
4
+ /Gemfile.lock
5
+ /rdoc/
6
+ /doc/
7
+ /tmp/
8
+ /pkg/
9
+ /log/
10
+ /lib/github_cli/man/u
11
+
12
+ # rspec failure tracking
13
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
@@ -0,0 +1,6 @@
1
+ ---
2
+ language: ruby
3
+ cache: bundler
4
+ rvm:
5
+ - 2.6.3
6
+ before_install: gem install bundler -v 2.1.2
@@ -0,0 +1,6 @@
1
+ # Change log
2
+
3
+ ## [v0.0.1] - 2020-11-08
4
+
5
+ ### Added
6
+ * Added core command: `carlo`
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in monte.gemspec
4
+ gemspec
5
+
6
+ gem "rake", "~> 12.0"
7
+ gem "rspec", "~> 3.0"
@@ -0,0 +1,20 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2020 Andrew Werner
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+ this software and associated documentation files (the "Software"), to deal in
7
+ the Software without restriction, including without limitation the rights to
8
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9
+ the Software, and to permit persons to whom the Software is furnished to do so,
10
+ subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,65 @@
1
+ # Monte
2
+
3
+ Monte is a simple gem designed to help software engineers and developers answer
4
+ the question 'When will it be done?'.
5
+
6
+ The tool uses the Monte Carlo method to provide a reasonable forecast of the
7
+ future based on statistically relevant data from the past.
8
+ ## Installation
9
+
10
+ You must have ruby installed to use this tool.
11
+
12
+ start by installing the gem.
13
+
14
+ ```sh
15
+ $ gem install monte
16
+ ```
17
+
18
+ ## Usage
19
+
20
+ The following is the simplest version of Monte which should be used when you
21
+ have little previous data with which to build your forecast.
22
+
23
+ ```sh
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
+ > └──────────┴──────────┴──────────┴──────────┴──────────┴──────────┴──────────┘
49
+ ```
50
+
51
+ ## Development
52
+
53
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run
54
+ `rake spec` to run the tests. You can also run `bin/console` for an interactive
55
+ prompt that will allow you to experiment.
56
+
57
+ To install this gem onto your local machine, run `bundle exec rake install`. To
58
+ release a new version, update the version number in `version.rb`, and then run
59
+ `bundle exec rake release`, which will create a git tag for the version, push
60
+ git commits and tags, and push the `.gem` file to
61
+ [rubygems.org](https://rubygems.org).
62
+
63
+ ## Copyright
64
+
65
+ Copyright (c) 2020 Andrew Werner. See [MIT License](LICENSE.txt) for further details.
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "monte"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ lib_path = File.expand_path('../lib', __dir__)
5
+ $:.unshift(lib_path) if !$:.include?(lib_path)
6
+ require 'monte/cli'
7
+
8
+ Signal.trap('INT') do
9
+ warn("\n#{caller.join("\n")}: interrupted")
10
+ exit(1)
11
+ end
12
+
13
+ begin
14
+ Monte::CLI.start
15
+ rescue Monte::CLI::Error => err
16
+ puts "ERROR: #{err.message}"
17
+ exit 1
18
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'monte/version'
4
+
5
+ module Monte
6
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'thor'
4
+
5
+ module Monte
6
+ # Handle the application command line parsing
7
+ # and the dispatch to various command objects
8
+ #
9
+ # @api public
10
+ class CLI < Thor
11
+ # Error raised by this runner
12
+ Error = Class.new(StandardError)
13
+
14
+ desc 'version', 'monte version'
15
+ def version
16
+ require_relative 'version'
17
+ puts "v#{Monte::VERSION}"
18
+ end
19
+ map %w[--version -v] => :version
20
+
21
+ desc 'carlo', 'Runs through a set of questions to generate a forecast for project completion'
22
+ method_option :help, aliases: '-h', type: :boolean,
23
+ desc: 'Display usage information'
24
+ def carlo
25
+ if options[:help]
26
+ invoke :help, ['carlo']
27
+ else
28
+ require_relative 'commands/carlo'
29
+ Monte::Commands::Carlo.new(options).execute
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,131 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'forwardable'
4
+
5
+ module Monte
6
+ class Command
7
+ extend Forwardable
8
+
9
+ def_delegators :command, :run
10
+
11
+ # Execute this command
12
+ #
13
+ # @api public
14
+ def execute(*)
15
+ raise(
16
+ NotImplementedError,
17
+ "#{self.class}##{__method__} must be implemented"
18
+ )
19
+ end
20
+
21
+ # The external commands runner
22
+ #
23
+ # @see http://www.rubydoc.info/gems/tty-command
24
+ #
25
+ # @api public
26
+ def command(**options)
27
+ require 'tty-command'
28
+ TTY::Command.new(options)
29
+ end
30
+
31
+ # The cursor movement
32
+ #
33
+ # @see http://www.rubydoc.info/gems/tty-cursor
34
+ #
35
+ # @api public
36
+ def cursor
37
+ require 'tty-cursor'
38
+ TTY::Cursor
39
+ end
40
+
41
+ # Open a file or text in the user's preferred editor
42
+ #
43
+ # @see http://www.rubydoc.info/gems/tty-editor
44
+ #
45
+ # @api public
46
+ def editor
47
+ require 'tty-editor'
48
+ TTY::Editor
49
+ end
50
+
51
+ # File manipulation utility methods
52
+ #
53
+ # @see http://www.rubydoc.info/gems/tty-file
54
+ #
55
+ # @api public
56
+ def generator
57
+ require 'tty-file'
58
+ TTY::File
59
+ end
60
+
61
+ # Terminal output paging
62
+ #
63
+ # @see http://www.rubydoc.info/gems/tty-pager
64
+ #
65
+ # @api public
66
+ def pager(**options)
67
+ require 'tty-pager'
68
+ TTY::Pager.new(options)
69
+ end
70
+
71
+ # Terminal platform and OS properties
72
+ #
73
+ # @see http://www.rubydoc.info/gems/tty-pager
74
+ #
75
+ # @api public
76
+ def platform
77
+ require 'tty-platform'
78
+ TTY::Platform.new
79
+ end
80
+
81
+ # The interactive prompt
82
+ #
83
+ # @see http://www.rubydoc.info/gems/tty-prompt
84
+ #
85
+ # @api public
86
+ def prompt(**options)
87
+ require 'tty-prompt'
88
+ TTY::Prompt.new(options)
89
+ end
90
+
91
+ # Get terminal screen properties
92
+ #
93
+ # @see http://www.rubydoc.info/gems/tty-screen
94
+ #
95
+ # @api public
96
+ def screen
97
+ require 'tty-screen'
98
+ TTY::Screen
99
+ end
100
+
101
+ # The unix which utility
102
+ #
103
+ # @see http://www.rubydoc.info/gems/tty-which
104
+ #
105
+ # @api public
106
+ def which(*args)
107
+ require 'tty-which'
108
+ TTY::Which.which(*args)
109
+ end
110
+
111
+ # Check if executable exists
112
+ #
113
+ # @see http://www.rubydoc.info/gems/tty-which
114
+ #
115
+ # @api public
116
+ def exec_exist?(*args)
117
+ require 'tty-which'
118
+ TTY::Which.exist?(*args)
119
+ end
120
+
121
+ def table(headers, rows)
122
+ require 'tty-table'
123
+ TTY::Table.new(headers, rows)
124
+ end
125
+
126
+ def large_title(title)
127
+ require 'tty-font'
128
+ TTY::Font.new(:standard).write(title)
129
+ end
130
+ end
131
+ end
@@ -0,0 +1 @@
1
+ #
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'date'
4
+ require_relative '../command'
5
+ require_relative '../simulation'
6
+
7
+ module Monte
8
+ module Commands
9
+ # Runs Monte Carlo Simulation to estimate how long a piece of work will take
10
+ class Carlo < Monte::Command
11
+ include Simulation
12
+ CERTAINTY = { 'low' => 1.8, 'medium' => 1.5, 'high' => 1.2 }.freeze
13
+ RUNS = { '10000' => 10_000, '1000' => 1000, '500' => 500 }.freeze
14
+ HEADERS = ['5%', '15%', '30%', '50%', '70%', '85%', '95%'].freeze
15
+ PERCENTILES = [0.05, 0.15, 0.3, 0.5, 0.7, 0.85, 0.95].freeze
16
+
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!
25
+ results = percentiles(user_input)
26
+ output.puts("\n\nYour Results\n\n")
27
+ output.puts(create_table(results))
28
+ end
29
+
30
+ def create_table(rows)
31
+ table(HEADERS, [rows]).render(:unicode, alignment: [:center])
32
+ end
33
+
34
+ def create_header
35
+ large_title('Monte')
36
+ end
37
+
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)
49
+ end
50
+ end
51
+
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ # The code to run n monte carlo simulations
4
+ # args is a hash that must contain the following:
5
+ # :backlog
6
+ # :split_factor
7
+ # :runs
8
+ # :low
9
+ # :high
10
+ module Simulation
11
+ PERCENTILES = [0.05, 0.15, 0.3, 0.5, 0.7, 0.85, 0.95].freeze
12
+
13
+ def percentiles(args)
14
+ results = run_simulations(args).sort
15
+ PERCENTILES.map do |percentile|
16
+ index = args[:runs] * (percentile - 1)
17
+ results[index]
18
+ end
19
+ end
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(
25
+ estimated_backlog,
26
+ args[:low],
27
+ args[:high]
28
+ ) * 7
29
+ end
30
+ end
31
+
32
+ def simulate(backlog, low, high, result = 0)
33
+ return result if backlog <= 0
34
+
35
+ simulate(
36
+ backlog - rand(low..high),
37
+ low,
38
+ high,
39
+ result + 1
40
+ )
41
+ end
42
+ end
@@ -0,0 +1,3 @@
1
+ module Monte
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lib/monte/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'monte'
7
+ spec.license = 'MIT'
8
+ spec.version = Monte::VERSION
9
+ spec.authors = ['Andrew Werner']
10
+ spec.email = ['awerner1@googlemail.com']
11
+
12
+ spec.summary = 'Monte Carlo forecasting for engineering projects'
13
+ spec.description = "If you are an engineer who is being asked, 'When will
14
+ it be done?' then Monte can help by using the Monte Carlo method to provide
15
+ you with plausile forecasts based on historic data."
16
+ spec.homepage = 'https://github.com/ALRW/monte'
17
+ spec.required_ruby_version = Gem::Requirement.new('>= 2.3.0')
18
+
19
+ spec.metadata['homepage_uri'] = spec.homepage
20
+ spec.metadata['source_code_uri'] = 'https://github.com/ALRW/monte.git'
21
+ spec.metadata['changelog_uri'] = 'https://github.com/ALRW/monte/blob/master/CHANGELOG.md'
22
+
23
+ # Specify which files should be added to the gem when it is released.
24
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
25
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
26
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
27
+ end
28
+ spec.bindir = 'exe'
29
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
30
+ spec.require_paths = ['lib']
31
+ end
metadata ADDED
@@ -0,0 +1,69 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: monte
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Andrew Werner
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2020-11-15 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: |-
14
+ If you are an engineer who is being asked, 'When will
15
+ it be done?' then Monte can help by using the Monte Carlo method to provide
16
+ you with plausile forecasts based on historic data.
17
+ email:
18
+ - awerner1@googlemail.com
19
+ executables:
20
+ - monte
21
+ extensions: []
22
+ extra_rdoc_files: []
23
+ files:
24
+ - ".gitignore"
25
+ - ".rspec"
26
+ - ".travis.yml"
27
+ - CHANGELOG.md
28
+ - Gemfile
29
+ - LICENSE.txt
30
+ - README.md
31
+ - Rakefile
32
+ - bin/console
33
+ - bin/setup
34
+ - exe/monte
35
+ - lib/monte.rb
36
+ - lib/monte/cli.rb
37
+ - lib/monte/command.rb
38
+ - lib/monte/commands/.gitkeep
39
+ - lib/monte/commands/carlo.rb
40
+ - lib/monte/simulation.rb
41
+ - lib/monte/version.rb
42
+ - monte.gemspec
43
+ homepage: https://github.com/ALRW/monte
44
+ licenses:
45
+ - MIT
46
+ metadata:
47
+ homepage_uri: https://github.com/ALRW/monte
48
+ source_code_uri: https://github.com/ALRW/monte.git
49
+ changelog_uri: https://github.com/ALRW/monte/blob/master/CHANGELOG.md
50
+ post_install_message:
51
+ rdoc_options: []
52
+ require_paths:
53
+ - lib
54
+ required_ruby_version: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ version: 2.3.0
59
+ required_rubygems_version: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: '0'
64
+ requirements: []
65
+ rubygems_version: 3.1.2
66
+ signing_key:
67
+ specification_version: 4
68
+ summary: Monte Carlo forecasting for engineering projects
69
+ test_files: []