analytics_charts 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 239faf9fca54620fbff0cfac86d9f53e7b83b371
4
+ data.tar.gz: 639fa9f5fa16c0999fbec645c1587e2f94514bdd
5
+ SHA512:
6
+ metadata.gz: 77b264d681853bf1c30a5996365b3e326956e0a29bb0926a3aa4cb6caa5947dc0e92202ee61ed7316b6022c61ee592a9ea66b3c1003570e9fea138c624a2c848
7
+ data.tar.gz: 7b7a7a3e50b27f3bc1bd2aa97c82a80c2ab5c2d828b9fcb67f3a5d34f4a44f19e96d77a58baab2ebb54d5eae4e3ae5c7f6d30d831fba571d300066ab6addce69
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in analytics_charts.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 STEPHEN YU
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,44 @@
1
+ # AnalyticsCharts
2
+
3
+ Custom library for a pie chart. Originated from the gruff library, but branched out.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'analytics_charts'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install analytics_charts
18
+
19
+ ## Usage
20
+
21
+ #!/usr/bin/env ruby
22
+ require 'analytics-charts'
23
+ g = AnalyticsCharts::CustomPie.new('./yourImage.png')
24
+ hash = {
25
+ 'fill' => 'blue',
26
+ 'font_family' => 'Helvetica',
27
+ 'pointsize' => 64,
28
+ 'font_weight' => 700
29
+ }
30
+
31
+ g.set_pie_geometry(1242,620, 250)
32
+ g.insert_pie_data("ndaf me3", 15.1,1)
33
+ g.insert_pie_data("ndaf me4", 15,3)
34
+ g.insert_pie_data("ndaf me2", 15.7,2)
35
+ g.insert_pie_data("ndaf me1", 300.22,0)
36
+ g.write
37
+
38
+ ## Contributing
39
+
40
+ 1. Fork it ( http://github.com/tryceattack/analytics_charts/fork )
41
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
42
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
43
+ 4. Push to the branch (`git push origin my-new-feature`)
44
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'analytics_charts/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "analytics_charts"
8
+ spec.version = AnalyticsCharts::VERSION
9
+ spec.authors = ["STEPHEN YU"]
10
+ spec.email = ["istephenyu@gmail.com"]
11
+ spec.summary = %q{Chart for analytics_charts}
12
+ spec.description = %q{Initially wrote code on top of gruff library. But eventually to do
13
+ more fined-tuned data rendering and the need for more lightweight code, I branched out to
14
+ this new library. Source for original code of gruff library.
15
+ https://github.com/topfunky/gruff/}
16
+ spec.homepage = ""
17
+ spec.license = "MIT"
18
+
19
+ spec.files = `git ls-files -z`.split("\x0")
20
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
21
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
22
+ spec.require_paths = ["lib"]
23
+
24
+ spec.add_development_dependency "bundler", "~> 1.5"
25
+ spec.add_development_dependency "rake"
26
+ spec.add_dependency 'rmagick'
27
+ end
data/appendix.md ADDED
@@ -0,0 +1,20 @@
1
+ if small_slice_index[0] == 1 and small_slice_index[1] == 1
2
+ label_offset_degrees[0] = -15
3
+ label_offset_degrees[1] = 15
4
+ label_offset_degrees[2] = 15
5
+ label_offset_degrees[3] = -15
6
+ elsif small_slice_index[1] == 1 and small_slice_index[2] == 1
7
+ label_offset_degrees[0] = -15
8
+ label_offset_degrees[1] = -15
9
+ label_offset_degrees[2] = 15
10
+ label_offset_degrees[3] = 15
11
+ elsif small_slice_index[2] == 1 and small_slice_index[3] == 1
12
+ label_offset_degrees[0] = 15
13
+ label_offset_degrees[1] = -15
14
+ label_offset_degrees[2] = -15
15
+ label_offset_degrees[3] = 15
16
+ elsif small_slice_index[3] == 1 and small_slice_index[0] == 1
17
+ label_offset_degrees[0] = 15
18
+ label_offset_degrees[1] = 15
19
+ label_offset_degrees[2] = -15
20
+ label_offset_degrees[3] = -15
@@ -0,0 +1,3 @@
1
+ module AnalyticsCharts
2
+ VERSION = "0.0.1"
3
+ end
data/lib/custom_pie.rb ADDED
@@ -0,0 +1,211 @@
1
+ require 'RMagick'
2
+
3
+ class AnalyticsCharts::CustomPie
4
+ include Magick
5
+ attr_accessor :pie_center_x
6
+ attr_accessor :pie_center_y
7
+ attr_accessor :pie_radius
8
+
9
+ def initialize(image_path)
10
+ @base_image = Image.read(image_path)[0]
11
+ @d = Draw.new
12
+ @data = Hash.new # Value is array with two items
13
+ @aggregate = Array([0,0,0,0]) # Cluster brands into categories
14
+ @columns = @base_image.columns
15
+ @rows = @base_image.rows
16
+ set_pie_colors(%w(#AD1F25 #BE6428 #C1B630 #1E753B))
17
+ end
18
+
19
+ def set_pie_geometry(x, y, radius)
20
+ @pie_center_x = x
21
+ @pie_center_y = y
22
+ @pie_radius = radius
23
+ end
24
+
25
+ def set_pie_colors(list)
26
+ @colors = list
27
+ end
28
+
29
+ def insert_pie_data(name, amount, quality)
30
+ #convert all '' instances to an apostrophe
31
+ name = name.gsub(/'/, "\'")
32
+ @data[name] = [amount, quality]
33
+ @aggregate[quality] += amount
34
+ end
35
+
36
+ def insert_text(x_offset, y_offset, text, features = {})
37
+ features.each { |feature, attribute|
38
+ set_feature(feature, attribute)
39
+ }
40
+ #puts @d.get_type_metrics(@base_image, text.to_s).width,@d.get_type_metrics(@base_image, text.to_s).height,text
41
+ @d.annotate(@base_image, 0 ,0, x_offset, y_offset, text)
42
+ end
43
+
44
+ def draw
45
+ if @data.size > 0
46
+ @d = Draw.new
47
+ @d.stroke_width(@pie_radius)
48
+ total_sum = 0.0
49
+ prev_degrees = 60.0
50
+ @d.fill_opacity(0) # VERY IMPORTANT, otherwise undesired artifact can result.
51
+ total_sum = @aggregate.inject(:+) + 0.0 # Sum elements and make it a float
52
+ degrees = Array([0,0,0,0])
53
+ label_offset_degrees = Array([0,0,0,0])
54
+ @aggregate.each_with_index do |data_row, index|
55
+ degrees[index] = (data_row / total_sum) * 360.0
56
+ end
57
+ num_small_slices = 0
58
+ small_slice_index = Array([0,0,0,0])
59
+ for i in 0..3
60
+ if degrees[i] != 0 and degrees[i] < 18.0
61
+ num_small_slices += 1
62
+ small_slice_index[i] = 1
63
+ end
64
+ end
65
+ for i in 0..3 # First draw slices
66
+ next if degrees[i] == 0
67
+ @d = @d.stroke @colors[i]
68
+ # ellipse will draw the the stroke centered on the first two parameters offset by the second two.
69
+ # therefore, in order to draw a circle of the proper diameter we must center the stroke at
70
+ # half the radius for both x and y
71
+ @d = @d.ellipse(@pie_center_x, @pie_center_y,
72
+ @pie_radius / 2.0, @pie_radius / 2.0,
73
+ prev_degrees, prev_degrees + degrees[i] + 0.5) # <= +0.5 'fudge factor' gets rid of the ugly gaps
74
+ prev_degrees += degrees[i]
75
+ end
76
+ # If less than two small slices, or there are two small slices that are not adjacent
77
+ if num_small_slices < 2 or (num_small_slices == 2 and small_slice_index[0] == small_slice_index[2])
78
+ #Do nothing
79
+ # If two adjacent small slices, push them apart. Non-adjacent case is taken care of above.
80
+ # I also push back the other labels too. The logic is condensed. To see original logic,
81
+ # consult appendix.html
82
+ elsif num_small_slices == 2
83
+ if small_slice_index[1] == 1
84
+ label_offset_degrees[0] = -15
85
+ label_offset_degrees[2] = 15
86
+ else
87
+ label_offset_degrees[0] = 15
88
+ label_offset_degrees[2] = -15
89
+ end
90
+ if small_slice_index[2] == 1
91
+ label_offset_degrees[1] = -15
92
+ label_offset_degrees[3] = 15
93
+ else
94
+ label_offset_degrees[1] = 15
95
+ label_offset_degrees[3] = -15
96
+ end
97
+ # In this case, push apart only the outside small slices.
98
+ elsif num_small_slices == 3
99
+ if small_slice_index[0] == 0
100
+ label_offset_degrees[1] = -15
101
+ label_offset_degrees[3] = 15
102
+ elsif small_slice_index[1] == 0
103
+ label_offset_degrees[2] = -15
104
+ label_offset_degrees[0] = 15
105
+ elsif small_slice_index[2] == 0
106
+ label_offset_degrees[3] = -15
107
+ label_offset_degrees[1] = 15
108
+ elsif small_slice_index[3] == 0
109
+ label_offset_degrees[0] = -15
110
+ label_offset_degrees[2] = 15
111
+ end
112
+ end
113
+ prev_degrees = 60.0 # Now focus on labels
114
+ @aggregate.each_with_index do |data_row, i|
115
+ next if degrees[i] == 0
116
+ half_angle = prev_degrees + degrees[i] / 2
117
+ label_string = '$' + data_row.to_s
118
+ draw_pie_label(@pie_center_x,@pie_center_y, half_angle + label_offset_degrees[i],
119
+ @pie_radius, label_string)
120
+ prev_degrees += degrees[i]
121
+ end
122
+ @d.draw(@base_image)
123
+ end
124
+ end
125
+
126
+ def write(filename='graph.png')
127
+ draw
128
+ draw_labels
129
+ @base_image.write(filename)
130
+ end
131
+
132
+ def draw_labels
133
+ @d.pointsize = 56
134
+ @d.align = LeftAlign
135
+ sorted_data = @data.sort_by{|key,value| -value[1]} # Sort by descending quality
136
+ x_offset = 180 #HARD CODED, fix later
137
+ y_offset = 440 #HARD CODED
138
+ for data in sorted_data
139
+ if data[1][0] > 0 # Amount > 0
140
+ font_weight = 900 # Very Bold
141
+ text = data[0] + ' <--'
142
+ else
143
+ text = data[0]
144
+ font_weight = 900 # Normal
145
+ end
146
+ case data[1][1]
147
+ when 3
148
+ insert_text(x_offset, y_offset, text, {'fill'=> '#1E753B', 'font_weight'=> font_weight })
149
+ when 2
150
+ insert_text(x_offset, y_offset, text, {'fill'=> '#C1B630', 'font_weight'=> font_weight })
151
+ when 1
152
+ insert_text(x_offset, y_offset, text, {'fill'=> '#BE6428', 'font_weight'=> font_weight })
153
+ when 0
154
+ insert_text(x_offset, y_offset, text, {'fill'=> '#AD1F25', 'font_weight'=> font_weight })
155
+ end
156
+ y_offset += 60 #HARD CODED, fix later
157
+ end
158
+ end
159
+
160
+ def draw_pie_label(center_x, center_y, angle, radius, percent)
161
+ @d.pointsize = 56 #HARD CODED, fix later
162
+ width = @d.get_type_metrics(@base_image, percent.to_s).width
163
+ ascent = @d.get_type_metrics(@base_image, percent.to_s).ascent
164
+ descent = @d.get_type_metrics(@base_image, percent.to_s).descent
165
+ radians = angle * Math::PI / 180.0
166
+ x = center_x + radius * Math.cos(radians)
167
+ # By default, text is centered at bottom, so need to shift vertically to center it
168
+ y = center_y + ascent / 2.0 + radius * Math.sin(radians)
169
+ # Imagine the text box around the text
170
+ # Shift text box so a corner is tangent to circle
171
+ if x > center_x
172
+ x += width / 2.0
173
+ end
174
+ if x < center_x
175
+ x -= width / 2.0
176
+ end
177
+ if y > center_y
178
+ y += ascent / 2.0
179
+ end
180
+ if y < center_y
181
+ y -= (ascent / 2.0 - descent) # descent to account for '$' descent,
182
+ # descent value retrieved is negative, so sub instead of add
183
+ end
184
+
185
+ @d.align = CenterAlign
186
+ insert_text(x, y, percent, {'fill'=> 'black', 'font_weight'=> 700})
187
+ end
188
+
189
+ def set_feature(feature, attribute)
190
+ case feature
191
+ when 'fill'
192
+ @d.fill = attribute
193
+ when 'font'
194
+ @d.font = attribute
195
+ when 'font_family'
196
+ @d.font_family = attribute
197
+ when 'font_stretch'
198
+ @d.font_stretch = attribute
199
+ when 'font_style'
200
+ @d.font_style = attribute
201
+ when 'font_weight'
202
+ @d.font_weight = attribute
203
+ when 'stroke'
204
+ @d.stroke = attribute
205
+ when 'pointsize'
206
+ @d.pointsize = attribute
207
+ when 'text_undercolor'
208
+ @d.undercolor = attribute
209
+ end
210
+ end
211
+ end
metadata ADDED
@@ -0,0 +1,99 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: analytics_charts
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - STEPHEN YU
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-05-06 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.5'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.5'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rmagick
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description: |-
56
+ Initially wrote code on top of gruff library. But eventually to do
57
+ more fined-tuned data rendering and the need for more lightweight code, I branched out to
58
+ this new library. Source for original code of gruff library.
59
+ https://github.com/topfunky/gruff/
60
+ email:
61
+ - istephenyu@gmail.com
62
+ executables: []
63
+ extensions: []
64
+ extra_rdoc_files: []
65
+ files:
66
+ - .gitignore
67
+ - Gemfile
68
+ - LICENSE.txt
69
+ - README.md
70
+ - Rakefile
71
+ - analytics_charts.gemspec
72
+ - appendix.md
73
+ - lib/analytics_charts/version.rb
74
+ - lib/custom_pie.rb
75
+ homepage: ''
76
+ licenses:
77
+ - MIT
78
+ metadata: {}
79
+ post_install_message:
80
+ rdoc_options: []
81
+ require_paths:
82
+ - lib
83
+ required_ruby_version: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - '>='
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ required_rubygems_version: !ruby/object:Gem::Requirement
89
+ requirements:
90
+ - - '>='
91
+ - !ruby/object:Gem::Version
92
+ version: '0'
93
+ requirements: []
94
+ rubyforge_project:
95
+ rubygems_version: 2.1.9
96
+ signing_key:
97
+ specification_version: 4
98
+ summary: Chart for analytics_charts
99
+ test_files: []