defect-density-heatmap 0.0.1

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.
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in defect-density-heatmap.gemspec
4
+ gemspec
data/Guardfile ADDED
@@ -0,0 +1,8 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ guard 'rspec', :version => 2 do
5
+ watch(%r{^spec/.+_spec\.rb$})
6
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
7
+ watch('spec/spec_helper.rb') { "spec" }
8
+ end
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ desc "Run all specs"
4
+ Spec::Rake::SpecTask.new(:spec) do |t|
5
+ t.spec_files = FileList['spec/**/*_spec.rb']
6
+ t.spec_opts = ['--color']
7
+ end
data/bin/heatmap ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ code_directory = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
4
+ $LOAD_PATH.unshift(code_directory) unless $LOAD_PATH.include?(code_directory)
5
+ require 'command_line_runner'
6
+ CommandLineRunner.run
@@ -0,0 +1,25 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "heatmap/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "defect-density-heatmap"
7
+ s.version = Heatmap::VERSION
8
+ s.authors = ["Brandon Carlson"]
9
+ s.email = ["bcarlso@gmail.com"]
10
+ s.homepage = "http://github.com/bcarlso/defect-density-heatmap"
11
+ s.summary = %q{Generates a 'heatmap' from files changed in the SCM system.}
12
+ s.description = %q{Generates a tag cloud based on project file churn.}
13
+
14
+ s.rubyforge_project = "defect-density-heatmap"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ # specify any dependencies here; for example:
22
+ s.add_development_dependency "rspec"
23
+ s.add_development_dependency "guard"
24
+ # s.add_runtime_dependency "rest-client"
25
+ end
@@ -0,0 +1,34 @@
1
+ require 'optparse'
2
+ require 'heatmap_formatter'
3
+
4
+ class CommandLineRunner
5
+ def self.run
6
+ options = {
7
+ :input_stream => STDIN,
8
+ :output_stream => STDOUT,
9
+ :regular_expression => "(?<total>(\\d+)),(?<defects>(\\d+)),(?<filename>(.+))"
10
+ }
11
+
12
+ OptionParser.new do | opts |
13
+ opts.banner = "Usage: heatmap [options]"
14
+
15
+ opts.on("-r", "--record-format pattern", "Regular expression with named captures describing the record") do | regex |
16
+ options[:regular_expression] = regex
17
+ end
18
+
19
+ opts.on("-f", "--file inputfile", "File containing commit data") do | filename, f2 |
20
+ options[:input_stream] = File.new(filename, 'r')
21
+ end
22
+
23
+ opts.on("-o", "--outfile outputfile", "File to write output to") do | filename |
24
+ options[:output_stream] = File.new(filename, 'w')
25
+ end
26
+ end.parse!
27
+
28
+ formatter = HeatmapFormatter.new(options[:regular_expression],
29
+ options[:input_stream],
30
+ options[:output_stream])
31
+
32
+ formatter.render
33
+ end
34
+ end
@@ -0,0 +1,5 @@
1
+ require 'heatmap/version'
2
+
3
+ module Heatmap
4
+ # Your code goes here...
5
+ end
data/lib/heatmap.rb ADDED
@@ -0,0 +1,5 @@
1
+ require "heatmap/version"
2
+
3
+ module Heatmap
4
+ # Your code goes here...
5
+ end
@@ -0,0 +1,3 @@
1
+ module Heatmap
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,71 @@
1
+ class HeatmapFormatter
2
+ def initialize(line_format, input_stream, output_stream)
3
+ @output_stream = output_stream
4
+
5
+ format = Regexp.new(line_format)
6
+
7
+ items_to_render = parse(input_stream, format)
8
+ @entries = items_to_render.sort do | first, second |
9
+ File.basename(first[:filename]) <=> File.basename(second[:filename])
10
+ end
11
+
12
+ @floor, ceiling = (@entries.collect { | e | e[:total].to_i }).minmax
13
+ @range = (ceiling - @floor)
14
+ end
15
+
16
+ def render
17
+ tags = @entries.inject("") do | result, e |
18
+ result + format_entry(e[:filename], e[:total].to_i, e[:defects])
19
+ end
20
+
21
+ html = <<-EOS
22
+ <html>
23
+ <head>
24
+ <title>SCM Heatmap</title>
25
+ <style type='text/css'>
26
+ body { font-family: sans-serif; }
27
+ ol li { display: inline; margin: 2px; }
28
+ </style>
29
+ </head>
30
+ <body>
31
+ <ol>
32
+ #{tags}
33
+ </ol>
34
+ </body>
35
+ </html>
36
+ EOS
37
+
38
+ @output_stream << html
39
+ end
40
+
41
+ def format_entry(filename, changes, defects)
42
+ style = compute_style(changes, defects)
43
+ title = "#{File.dirname(filename)} Changes: #{changes} Defects: #{defects}"
44
+ "<li style='#{style}' title='#{title}'>#{File.basename(filename)} </li>"
45
+ end
46
+
47
+ def size_for(number)
48
+ ((((number - @floor).to_f / @range) * 10).to_i * 3) + 6
49
+ end
50
+
51
+ private
52
+
53
+ def parse(stream, format)
54
+ stream.collect() do | line |
55
+ format.match(line)
56
+ end
57
+ end
58
+
59
+ def compute_style(total_changes, total_defects)
60
+ transparency = calculate_transparency(total_changes, total_defects)
61
+ "font-size: #{size_for(total_changes)}; background-color: rgba(255, 0, 0, #{transparency});"
62
+ end
63
+
64
+ def determine_relative_size_for(value)
65
+ @size_calculator.size_for(value)
66
+ end
67
+
68
+ def calculate_transparency(total_changes, total_defects)
69
+ total_defects.to_f / total_changes.to_f
70
+ end
71
+ end
@@ -0,0 +1,56 @@
1
+ 21,21,src/main/java/com/example/File1.java
2
+ 8,2,src/main/java/com/example/File2.java
3
+ 3,0,src/main/java/com/example/File3.java
4
+ 23,10,src/main/java/com/example/File4.java
5
+ 22,17,src/main/java/com/example/File5.java
6
+ 21,13,src/main/java/com/example/File6.java
7
+ 20,12,src/main/java/com/example/File7.java
8
+ 16,16,src/main/java/com/example/File8.java
9
+ 20,12,src/main/java/com/example/File9.java
10
+ 15,9,src/main/java/com/example/File10.java
11
+ 3,1,src/main/java/com/example/File11.java
12
+ 3,3,src/main/java/com/example/File12.java
13
+ 11,11,src/main/java/com/example/File13.java
14
+ 9,7,src/main/java/com/example/File14.java
15
+ 9,6,src/main/java/com/example/File15.java
16
+ 15,11,src/main/java/com/example/File16.java
17
+ 4,4,src/main/java/com/example/File17.java
18
+ 2,0,src/main/java/com/example/File18.java
19
+ 20,20,src/main/java/com/example/File19.java
20
+ 4,0,src/main/java/com/example/File20.java
21
+ 1,0,src/main/java/com/example/File21.java
22
+ 11,0,src/main/java/com/example/File22.java
23
+ 21,11,src/main/java/com/example/File23.java
24
+ 8,5,src/main/java/com/example/File24.java
25
+ 20,2,src/main/java/com/example/File25.java
26
+ 11,8,src/main/java/com/example/File26.java
27
+ 1,0,src/main/java/com/example/File27.java
28
+ 22,14,src/main/java/com/example/File28.java
29
+ 22,16,src/main/java/com/example/File29.java
30
+ 17,10,src/main/java/com/example/File30.java
31
+ 17,8,src/main/java/com/example/File31.java
32
+ 24,16,src/main/java/com/example/File32.java
33
+ 8,4,src/main/java/com/example/File33.java
34
+ 9,0,src/main/java/com/example/File34.java
35
+ 1,0,src/main/java/com/example/File35.java
36
+ 14,8,src/main/java/com/example/File36.java
37
+ 12,6,src/main/java/com/example/File37.java
38
+ 24,6,src/main/java/com/example/File38.java
39
+ 11,10,src/main/java/com/example/File39.java
40
+ 8,4,src/main/java/com/example/File40.java
41
+ 10,10,src/main/java/com/example/File41.java
42
+ 19,4,src/main/java/com/example/File42.java
43
+ 8,1,src/main/java/com/example/File43.java
44
+ 8,2,src/main/java/com/example/File44.java
45
+ 11,7,src/main/java/com/example/File45.java
46
+ 21,4,src/main/java/com/example/File46.java
47
+ 22,19,src/main/java/com/example/File47.java
48
+ 12,8,src/main/java/com/example/File48.java
49
+ 5,0,src/main/java/com/example/File49.java
50
+ 19,6,src/main/java/com/example/File50.java
51
+ 16,12,src/main/java/com/example/File51.java
52
+ 3,1,src/main/java/com/example/File52.java
53
+ 1,0,src/main/java/com/example/File53.java
54
+ 1,1,src/main/java/com/example/File54.java
55
+ 16,2,src/main/java/com/example/File55.java
56
+ 5,3,src/main/java/com/example/File56.java
@@ -0,0 +1,60 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe HeatmapFormatter do
4
+ before(:each) do
5
+ example_data = <<-EOS
6
+ 3:0 src/main/resources/com/example/SomeFile.java: DE3981,DE4386
7
+ 12:6 src/main/java/com/example/AnotherFile.java: US11743
8
+ 6:6 src/main/java/com/example/YetAnotherFile.java: DE3317,DE4077,US12070
9
+ EOS
10
+
11
+ @input_stream = StringIO.new(example_data)
12
+ @output_stream = StringIO.new
13
+
14
+ @formatter = HeatmapFormatter.new("(?<total>(\\d+)):(?<defects>(\\d+)) (?<filename>(.+)):.+",
15
+ @input_stream,
16
+ @output_stream)
17
+ end
18
+
19
+ it "Orders the tag cloud based on filename" do
20
+ @formatter.render
21
+ @output_stream.string.should match(".*AnotherFile.*SomeFile.*YetAnotherFile")
22
+ end
23
+
24
+
25
+ it "Renders each file as a tag" do
26
+ results = @formatter.format_entry 'path/to/SomeFile.java', 3, 0
27
+ results.should match("<li style='.+'>SomeFile.java </li>")
28
+ end
29
+
30
+ it "Adds file details to the title attribute for display when hovered over" do
31
+ results = @formatter.format_entry 'src/main/java/com/example/SomeFile.java', 3, 2
32
+ results.should have_title_containing("src/main/java/com/example Changes: 3 Defects: 2")
33
+ end
34
+
35
+ it "Adjusts the font size of each tag relative to the number of changes in the file" do
36
+ file_with( :changes => 3 ).should have_font_size(6)
37
+ file_with( :changes => 6 ).should have_font_size(15)
38
+ file_with( :changes => 12 ).should have_font_size(36)
39
+ end
40
+
41
+ it "Adjusts the background color of each tag based on the number of changes that were defects" do
42
+ file_with( :changes => 3 ).should match("background-color: rgba\\(255, 0, 0, 0.0\\)")
43
+
44
+ file_with( :changes => 3,
45
+ :defects => 3 ).should match("background-color: rgba\\(255, 0, 0, 1.0\\)")
46
+
47
+ file_with( :changes => 10,
48
+ :defects => 5 ).should match("background-color: rgba\\(255, 0, 0, 0.5\\)")
49
+ end
50
+
51
+
52
+ it "Generates a range of font sizes based on a min/max" do
53
+ @formatter.size_for(12).should == 36
54
+ @formatter.size_for(11).should == 30
55
+ @formatter.size_for(8).should == 21
56
+ @formatter.size_for(7).should == 18
57
+ @formatter.size_for(4).should == 9
58
+ @formatter.size_for(3).should == 6
59
+ end
60
+ end
@@ -0,0 +1,32 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+
4
+ require 'rspec'
5
+ require 'heatmap_formatter'
6
+
7
+ RSpec.configure do | config |
8
+ config.color_enabled = true
9
+ end
10
+
11
+ RSpec::Matchers.define :have_font_size do | expected |
12
+ match do | actual |
13
+ actual =~ Regexp.new(".*font-size: #{expected};.*")
14
+ end
15
+ end
16
+
17
+ RSpec::Matchers.define :have_color do | expected |
18
+ match do | actual |
19
+ actual =~ Regexp.new(".*color: #{expected};.*")
20
+ end
21
+ end
22
+
23
+ RSpec::Matchers.define :have_title_containing do | expected |
24
+ match do | actual |
25
+ actual =~ Regexp.new(".*title='.*#{expected}.*'.*")
26
+ end
27
+ end
28
+
29
+ def file_with(details)
30
+ @formatter.format_entry 'SomeFile.java', details[:changes], details[:defects] || 0
31
+ end
32
+
metadata ADDED
@@ -0,0 +1,85 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: defect-density-heatmap
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Brandon Carlson
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-10-04 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: &70219516054120 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *70219516054120
25
+ - !ruby/object:Gem::Dependency
26
+ name: guard
27
+ requirement: &70219516053700 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *70219516053700
36
+ description: Generates a tag cloud based on project file churn.
37
+ email:
38
+ - bcarlso@gmail.com
39
+ executables:
40
+ - heatmap
41
+ extensions: []
42
+ extra_rdoc_files: []
43
+ files:
44
+ - .gitignore
45
+ - Gemfile
46
+ - Guardfile
47
+ - Rakefile
48
+ - bin/heatmap
49
+ - defect-density-heatmap.gemspec
50
+ - lib/command_line_runner.rb
51
+ - lib/defect-density-heatmap.rb
52
+ - lib/heatmap.rb
53
+ - lib/heatmap/version.rb
54
+ - lib/heatmap_formatter.rb
55
+ - spec/example-data.csv
56
+ - spec/heatmap_formatter_spec.rb
57
+ - spec/spec_helper.rb
58
+ homepage: http://github.com/bcarlso/defect-density-heatmap
59
+ licenses: []
60
+ post_install_message:
61
+ rdoc_options: []
62
+ require_paths:
63
+ - lib
64
+ required_ruby_version: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ required_rubygems_version: !ruby/object:Gem::Requirement
71
+ none: false
72
+ requirements:
73
+ - - ! '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ requirements: []
77
+ rubyforge_project: defect-density-heatmap
78
+ rubygems_version: 1.8.6
79
+ signing_key:
80
+ specification_version: 3
81
+ summary: Generates a 'heatmap' from files changed in the SCM system.
82
+ test_files:
83
+ - spec/example-data.csv
84
+ - spec/heatmap_formatter_spec.rb
85
+ - spec/spec_helper.rb