est 0.1 → 0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +2 -2
- data/Rakefile +1 -1
- data/assets/est-text.xsl +41 -0
- data/assets/est.xsd +0 -1
- data/assets/est.xsl +9 -1
- data/bin/est +19 -12
- data/est/first.est +19 -0
- data/features/cli.feature +33 -3
- data/lib/est.rb +2 -2
- data/lib/est/estimate.rb +12 -5
- data/lib/est/estimates.rb +26 -6
- data/lib/est/methods/champions.rb +15 -4
- data/lib/est/version.rb +1 -1
- data/test/test_est.rb +1 -3
- metadata +4 -2
data/README.md
CHANGED
@@ -19,8 +19,8 @@ Run it locally and read its output:
|
|
19
19
|
$ est --help
|
20
20
|
```
|
21
21
|
|
22
|
-
Every estimate should be in its own file, with `.
|
23
|
-
Here is an example of an estimate file for a simple web app:
|
22
|
+
Every estimate should be in its own file, with `.est` extension (YAML format).
|
23
|
+
Here is an example of an estimate file `simple.est` for a simple web app:
|
24
24
|
|
25
25
|
```yaml
|
26
26
|
date: 19-12-2014
|
data/Rakefile
CHANGED
data/assets/est-text.xsl
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
<?xml version="1.0"?>
|
2
|
+
<!--
|
3
|
+
* Copyright (c) 2014 TechnoPark Corp.
|
4
|
+
* Copyright (c) 2014 Yegor Bugayenko
|
5
|
+
*
|
6
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
7
|
+
* of this software and associated documentation files (the 'Software'), to deal
|
8
|
+
* in the Software without restriction, including without limitation the rights
|
9
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
10
|
+
* copies of the Software, and to permit persons to whom the Software is
|
11
|
+
* furnished to do so, subject to the following conditions:
|
12
|
+
*
|
13
|
+
* The above copyright notice and this permission notice shall be included in all
|
14
|
+
* copies or substantial portions of the Software.
|
15
|
+
*
|
16
|
+
* THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
17
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
18
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFINGEMENT. IN NO EVENT SHALL THE
|
19
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
20
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
21
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
22
|
+
* SOFTWARE.
|
23
|
+
-->
|
24
|
+
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
|
25
|
+
version="1.0" xmlns="http://www.w3.org/1999/xhtml">
|
26
|
+
<xsl:output method="text" omit-xml-declaration="yes"/>
|
27
|
+
<xsl:template match="/estimate">
|
28
|
+
<xsl:text>Total: </xsl:text>
|
29
|
+
<xsl:value-of select="total"/>
|
30
|
+
<xsl:text> </xsl:text>
|
31
|
+
<xsl:apply-templates select="ests/est"/>
|
32
|
+
</xsl:template>
|
33
|
+
<xsl:template match="ests/est">
|
34
|
+
<xsl:value-of select="date"/>
|
35
|
+
<xsl:text>: </xsl:text>
|
36
|
+
<xsl:value-of select="total"/>
|
37
|
+
<xsl:text> hours by </xsl:text>
|
38
|
+
<xsl:value-of select="author"/>
|
39
|
+
<xsl:text> </xsl:text>
|
40
|
+
</xsl:template>
|
41
|
+
</xsl:stylesheet>
|
data/assets/est.xsd
CHANGED
data/assets/est.xsl
CHANGED
@@ -23,7 +23,7 @@
|
|
23
23
|
-->
|
24
24
|
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
|
25
25
|
version="1.0" xmlns="http://www.w3.org/1999/xhtml">
|
26
|
-
<xsl:template match="/">
|
26
|
+
<xsl:template match="/estimate">
|
27
27
|
<xsl:text disable-output-escaping="yes"><!DOCTYPE html></xsl:text>
|
28
28
|
<html lang="en">
|
29
29
|
<head>
|
@@ -60,8 +60,16 @@
|
|
60
60
|
<xsl:text>Total: </xsl:text>
|
61
61
|
<xsl:value-of select="total"/>
|
62
62
|
</p>
|
63
|
+
<xsl:apply-templates select="ests/est"/>
|
63
64
|
</div>
|
64
65
|
</body>
|
65
66
|
</html>
|
66
67
|
</xsl:template>
|
68
|
+
<xsl:template match="ests/est">
|
69
|
+
<xsl:value-of select="date"/>
|
70
|
+
<xsl:text>: </xsl:text>
|
71
|
+
<strong><xsl:value-of select="total"/></strong>
|
72
|
+
<xsl:text> hours by </xsl:text>
|
73
|
+
<xsl:value-of select="author"/>
|
74
|
+
</xsl:template>
|
67
75
|
</xsl:stylesheet>
|
data/bin/est
CHANGED
@@ -48,7 +48,7 @@ opts = Slop.parse(ARGV, strict: true, help: true) do
|
|
48
48
|
on(
|
49
49
|
't',
|
50
50
|
'format',
|
51
|
-
'Format to use (xml|html)',
|
51
|
+
'Format to use (xml|html|text)',
|
52
52
|
argument: :required
|
53
53
|
)
|
54
54
|
end
|
@@ -69,16 +69,23 @@ Encoding.default_external = Encoding::UTF_8
|
|
69
69
|
Encoding.default_internal = Encoding::UTF_8
|
70
70
|
file = opts.file? ? File.new(opts[:file], 'w') : STDOUT
|
71
71
|
output = Est::Base.new(opts).xml
|
72
|
-
if opts[:format]
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
72
|
+
if opts[:format].nil? || opts[:format] == 'text'
|
73
|
+
xslt = File.join(
|
74
|
+
File.dirname(File.dirname(__FILE__)),
|
75
|
+
'assets', 'est-text.xsl'
|
76
|
+
)
|
77
|
+
output = Nokogiri::XSLT(File.read(xslt)).transform(Nokogiri::XML(output))
|
78
|
+
elsif opts[:format] == 'html'
|
79
|
+
Est.log.info 'using HTML format'
|
80
|
+
xslt = File.join(
|
81
|
+
File.dirname(File.dirname(__FILE__)),
|
82
|
+
'assets', 'est.xsl'
|
83
|
+
)
|
84
|
+
output = Nokogiri::XSLT(File.read(xslt)).transform(Nokogiri::XML(output))
|
85
|
+
elsif opts[:format] == 'xml'
|
86
|
+
Est.log.info 'using XML format'
|
87
|
+
else
|
88
|
+
fail 'invalid format, use html, text, or xml'
|
83
89
|
end
|
84
90
|
file << output
|
91
|
+
Est.log.info "output saved into #{file}"
|
data/est/first.est
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
date: 20-12-2014
|
2
|
+
author: Yegor Bugayenko
|
3
|
+
method: champions.pert
|
4
|
+
scope:
|
5
|
+
1: ruby gem scaffolding
|
6
|
+
2: integration tests with Cucumber
|
7
|
+
3: user documentation
|
8
|
+
4: first estimation model
|
9
|
+
5: more estimation models
|
10
|
+
6: continuous integration configs
|
11
|
+
champions:
|
12
|
+
4:
|
13
|
+
worst-case: 6
|
14
|
+
best-case: 1
|
15
|
+
most-likely: 2
|
16
|
+
5:
|
17
|
+
worst-case: 10
|
18
|
+
best-case: 2
|
19
|
+
most-likely: 4
|
data/features/cli.feature
CHANGED
@@ -11,10 +11,9 @@ Feature: Command Line Processing
|
|
11
11
|
When I run bin/est with "--version"
|
12
12
|
Then Exit code is zero
|
13
13
|
|
14
|
-
Scenario: Simple estimate calculating
|
14
|
+
Scenario: Simple estimate calculating, in XML
|
15
15
|
Given I have a "sample.est" file with content:
|
16
16
|
"""
|
17
|
-
id: 789
|
18
17
|
date: 19-08-2014
|
19
18
|
author: Yegor Bugayenko
|
20
19
|
method: champions.pert
|
@@ -38,11 +37,42 @@ Feature: Command Line Processing
|
|
38
37
|
best-case: 8
|
39
38
|
most-likely: 16
|
40
39
|
"""
|
41
|
-
When I run bin/est with "-v -d . -f out.xml"
|
40
|
+
When I run bin/est with "-v -d . -t xml -f out.xml"
|
42
41
|
Then Exit code is zero
|
43
42
|
And Stdout contains "reading ."
|
44
43
|
And XML file "out.xml" matches "/estimate[total='54']"
|
45
44
|
|
45
|
+
Scenario: Simple estimate calculating, in Text
|
46
|
+
Given I have a "sample.est" file with content:
|
47
|
+
"""
|
48
|
+
date: 19-08-2014
|
49
|
+
author: Yegor Bugayenko
|
50
|
+
method: champions.pert
|
51
|
+
scope:
|
52
|
+
1: basic Sinatra scaffolding
|
53
|
+
2: front-end HAML files
|
54
|
+
3: SASS stylesheet
|
55
|
+
4: five model classes with unit tests
|
56
|
+
5: PostgreSQL migrations
|
57
|
+
6: Cucumber tests for PostgreSQL
|
58
|
+
7: Capybara tests for HTML front
|
59
|
+
8: CasperJS tests
|
60
|
+
9: achieve 80% test coverage
|
61
|
+
champions:
|
62
|
+
7:
|
63
|
+
worst-case: 40
|
64
|
+
best-case: 10
|
65
|
+
most-likely: 18
|
66
|
+
4:
|
67
|
+
worst-case: 30
|
68
|
+
best-case: 8
|
69
|
+
most-likely: 16
|
70
|
+
"""
|
71
|
+
When I run bin/est with "-d ."
|
72
|
+
Then Exit code is zero
|
73
|
+
And Stdout contains "Total: 54"
|
74
|
+
And Stdout contains "2014-08-19: 54 hours by Yegor Bugayenko"
|
75
|
+
|
46
76
|
Scenario: Rejects unknown options
|
47
77
|
Given I have a "test.est" file with content:
|
48
78
|
"""
|
data/lib/est.rb
CHANGED
@@ -70,7 +70,7 @@ module Est
|
|
70
70
|
def xml
|
71
71
|
dir = @opts.dir? ? @opts[:dir] : Dir.pwd
|
72
72
|
Est.log.info "reading #{dir}"
|
73
|
-
estimates = Estimates.new(dir)
|
73
|
+
estimates = Estimates::Const.new(Estimates.new(dir))
|
74
74
|
sanitize(
|
75
75
|
Nokogiri::XML::Builder.new do |xml|
|
76
76
|
xml << "<?xml-stylesheet type='text/xsl' href='#{xsl}'?>"
|
@@ -78,7 +78,7 @@ module Est
|
|
78
78
|
xml.total estimates.total
|
79
79
|
xml.ests do
|
80
80
|
estimates.iterate.each do |est|
|
81
|
-
xml.est
|
81
|
+
xml.est do
|
82
82
|
xml.date est.date
|
83
83
|
xml.total est.total
|
84
84
|
xml.author est.author
|
data/lib/est/estimate.rb
CHANGED
@@ -40,11 +40,6 @@ module Est
|
|
40
40
|
fail "failed to read file #{file}" unless @yaml
|
41
41
|
end
|
42
42
|
|
43
|
-
# Get id.
|
44
|
-
def id
|
45
|
-
@yaml['id']
|
46
|
-
end
|
47
|
-
|
48
43
|
# Get date.
|
49
44
|
def date
|
50
45
|
Date.strptime(@yaml['date'], '%d-%m-%Y')
|
@@ -61,5 +56,17 @@ module Est
|
|
61
56
|
fail "unsupported method #{method}" unless method == 'champions.pert'
|
62
57
|
Champions.new(@yaml).total
|
63
58
|
end
|
59
|
+
|
60
|
+
# Constant estimate.
|
61
|
+
class Const
|
62
|
+
attr_reader :date, :author, :total
|
63
|
+
# Ctor.
|
64
|
+
# +est+:: Estimate
|
65
|
+
def initialize(est)
|
66
|
+
@date = est.date
|
67
|
+
@author = est.author
|
68
|
+
@total = est.total
|
69
|
+
end
|
70
|
+
end
|
64
71
|
end
|
65
72
|
end
|
data/lib/est/estimates.rb
CHANGED
@@ -42,16 +42,36 @@ module Est
|
|
42
42
|
# Get total estimate.
|
43
43
|
def total
|
44
44
|
estimates = iterate
|
45
|
-
|
46
|
-
|
45
|
+
estimates.reduce(0) do |a, e|
|
46
|
+
Est.log.info "#{e.date}/#{e.author}: #{e.total}"
|
47
|
+
a + e.total
|
48
|
+
end / estimates.size
|
47
49
|
end
|
48
50
|
|
49
51
|
# Iterate them all
|
50
52
|
def iterate
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
53
|
+
unless @iterate
|
54
|
+
@iterate = Dir.entries(@dir)
|
55
|
+
.reject { |f| f.index('.') == 0 }
|
56
|
+
.select { |f| f =~ /^.*\.est$/ }
|
57
|
+
.map { |f| File.join(@dir, f) }
|
58
|
+
.each { |f| Est.log.info "#{f} found" }
|
59
|
+
.map { |f| Estimate.new(f) }
|
60
|
+
.map { |f| Estimate::Const.new(f) }
|
61
|
+
end
|
62
|
+
fail "no .est files found in #{@dir}" if @iterate.empty?
|
63
|
+
@iterate
|
64
|
+
end
|
65
|
+
|
66
|
+
# Const estimates.
|
67
|
+
class Const
|
68
|
+
attr_reader :total, :iterate
|
69
|
+
# Ctor.
|
70
|
+
# +est+:: Original estimates
|
71
|
+
def initialize(est)
|
72
|
+
@iterate = est.iterate
|
73
|
+
@total = est.total
|
74
|
+
end
|
55
75
|
end
|
56
76
|
end
|
57
77
|
end
|
@@ -31,6 +31,7 @@ require 'yaml'
|
|
31
31
|
# License:: MIT
|
32
32
|
module Est
|
33
33
|
# Scope Champions.
|
34
|
+
# see http://www.technoparkcorp.com/innovations/scope-champions/
|
34
35
|
class Champions
|
35
36
|
# Ctor.
|
36
37
|
# +yaml+:: YAML config
|
@@ -40,11 +41,21 @@ module Est
|
|
40
41
|
|
41
42
|
# Get total estimate.
|
42
43
|
def total
|
43
|
-
|
44
|
+
n = @yaml['scope'].size
|
44
45
|
champs = @yaml['champions']
|
45
|
-
|
46
|
-
|
47
|
-
|
46
|
+
m = champs.size
|
47
|
+
k = 0.54
|
48
|
+
sum = champs.map do |i, e|
|
49
|
+
total = (e['best-case'].to_i +
|
50
|
+
e['worst'].to_i +
|
51
|
+
e['most-likely'].to_i * 4) / 6
|
52
|
+
Est.log.info "#{i}: (#{e['best-case']} + #{e['worst-case']} +"\
|
53
|
+
" #{e['most-likely']} * 4) / 6 = #{total}"
|
54
|
+
total
|
55
|
+
end.reduce(&:+)
|
56
|
+
total = sum * k * (n / m)
|
57
|
+
Est.log.info "#{sum} * #{k} * (#{n} / #{m}) = #{total}"
|
58
|
+
total.to_i
|
48
59
|
end
|
49
60
|
end
|
50
61
|
end
|
data/lib/est/version.rb
CHANGED
data/test/test_est.rb
CHANGED
@@ -38,7 +38,6 @@ class TestEst < Minitest::Test
|
|
38
38
|
File.write(
|
39
39
|
File.join(dir, 'sample.est'),
|
40
40
|
'''
|
41
|
-
id: 5
|
42
41
|
date: 12-07-2014
|
43
42
|
author: Yegor Bugayenko
|
44
43
|
method: champions.pert
|
@@ -70,8 +69,7 @@ class TestEst < Minitest::Test
|
|
70
69
|
'/estimate/@version',
|
71
70
|
'/estimate/@date',
|
72
71
|
'/estimate[total="54"]',
|
73
|
-
'/estimate/ests[count(est)=1]'
|
74
|
-
'/estimate/ests/est[@id="5"]'
|
72
|
+
'/estimate/ests[count(est)=1]'
|
75
73
|
]
|
76
74
|
)
|
77
75
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: est
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: '0.
|
4
|
+
version: '0.2'
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2014-12-
|
12
|
+
date: 2014-12-20 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: nokogiri
|
@@ -206,11 +206,13 @@ files:
|
|
206
206
|
- LICENSE.txt
|
207
207
|
- README.md
|
208
208
|
- Rakefile
|
209
|
+
- assets/est-text.xsl
|
209
210
|
- assets/est.xsd
|
210
211
|
- assets/est.xsl
|
211
212
|
- bin/est
|
212
213
|
- cucumber.yml
|
213
214
|
- est.gemspec
|
215
|
+
- est/first.est
|
214
216
|
- features/cli.feature
|
215
217
|
- features/gem_package.feature
|
216
218
|
- features/step_definitions/steps.rb
|