hawk-eye 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.
Files changed (59) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +1 -0
  3. data/README.md +2 -0
  4. data/Rakefile +26 -0
  5. data/app/assets/javascripts/hawk-eye.js.coffee +2 -0
  6. data/app/assets/javascripts/hawk-eye/base.js.coffee +17 -0
  7. data/app/assets/javascripts/hawk-eye/date-bin-variable.js.coffee +256 -0
  8. data/app/assets/javascripts/hawk-eye/test.js.coffee +256 -0
  9. data/app/assets/stylesheets/hawk-eye/graphs/_variable_bin.sass +18 -0
  10. data/app/assets/stylesheets/hawk-eye/hawk-eye.sass +2 -0
  11. data/app/assets/stylesheets/hawk-eye/test.sass +40 -0
  12. data/app/assets/stylesheets/hawk_eye/application.css +15 -0
  13. data/app/controllers/hawk_eye/application_controller.rb +14 -0
  14. data/app/helpers/hawk_eye/application_helper.rb +4 -0
  15. data/app/services/hawk_eye/graph/date_bin_helpers.rb +7 -0
  16. data/app/services/hawk_eye/graph/date_bin_variable.rb +89 -0
  17. data/app/views/hawk_eye/application/home.html.slim +15 -0
  18. data/app/views/layouts/hawk_eye/application.html.erb +14 -0
  19. data/config/routes.rb +3 -0
  20. data/lib/hawk-eye.rb +1 -0
  21. data/lib/hawk_eye.rb +7 -0
  22. data/lib/hawk_eye/engine.rb +5 -0
  23. data/lib/hawk_eye/version.rb +3 -0
  24. data/lib/tasks/hawk_eye_tasks.rake +4 -0
  25. data/spec/dummy/README.rdoc +28 -0
  26. data/spec/dummy/Rakefile +6 -0
  27. data/spec/dummy/app/assets/javascripts/application.js +13 -0
  28. data/spec/dummy/app/assets/stylesheets/application.css +15 -0
  29. data/spec/dummy/app/controllers/application_controller.rb +5 -0
  30. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  31. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  32. data/spec/dummy/bin/bundle +3 -0
  33. data/spec/dummy/bin/rails +4 -0
  34. data/spec/dummy/bin/rake +4 -0
  35. data/spec/dummy/bin/setup +29 -0
  36. data/spec/dummy/config.ru +4 -0
  37. data/spec/dummy/config/application.rb +32 -0
  38. data/spec/dummy/config/boot.rb +5 -0
  39. data/spec/dummy/config/database.yml +25 -0
  40. data/spec/dummy/config/environment.rb +5 -0
  41. data/spec/dummy/config/environments/development.rb +41 -0
  42. data/spec/dummy/config/environments/production.rb +79 -0
  43. data/spec/dummy/config/environments/test.rb +42 -0
  44. data/spec/dummy/config/initializers/assets.rb +11 -0
  45. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  46. data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
  47. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  48. data/spec/dummy/config/initializers/inflections.rb +16 -0
  49. data/spec/dummy/config/initializers/mime_types.rb +4 -0
  50. data/spec/dummy/config/initializers/session_store.rb +3 -0
  51. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  52. data/spec/dummy/config/locales/en.yml +23 -0
  53. data/spec/dummy/config/routes.rb +4 -0
  54. data/spec/dummy/config/secrets.yml +22 -0
  55. data/spec/dummy/public/404.html +67 -0
  56. data/spec/dummy/public/422.html +67 -0
  57. data/spec/dummy/public/500.html +66 -0
  58. data/spec/dummy/public/favicon.ico +0 -0
  59. metadata +247 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 21679c4d16ca79235bea0cbffd1234c722788621
4
+ data.tar.gz: d3ffe869b2b75ffc64177a6057178dedb25dcb92
5
+ SHA512:
6
+ metadata.gz: e6f4abe501fc7bd1fc742a9f2443b742e37e749cbe44a6e62aa03dd20edb5bac3466974405957cf309e2d87ae8f84a1075a2c04379dabb64e9b25f24cc3eb7cd
7
+ data.tar.gz: b8eba4492c36cee2054e9ab1576767775d8478c395eb147a3910b497ca4bf39c0a9cd31037a1a1b580c377460b8d871f3bc67f44fa1dca7eddd69d6f2f460a99
data/LICENSE ADDED
@@ -0,0 +1 @@
1
+ Copyright 2016 Companytools
@@ -0,0 +1,2 @@
1
+ HawkEye
2
+ ========================================
@@ -0,0 +1,26 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = 'rdoc'
11
+ rdoc.title = 'HawkEye'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.rdoc')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+ APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
18
+ load 'rails/tasks/engine.rake'
19
+
20
+
21
+ load 'rails/tasks/statistics.rake'
22
+
23
+
24
+
25
+ Bundler::GemHelper.install_tasks
26
+
@@ -0,0 +1,2 @@
1
+ #= require ./hawk-eye/base
2
+ #= require_directory ./hawk-eye
@@ -0,0 +1,17 @@
1
+ class HawkEye
2
+ constructor: ->
3
+ @setup_callbacks = {}
4
+
5
+ setup: (target = $(document))->
6
+ for setup_label, callbacks_of_label of @setup_callbacks
7
+ callbacks_of_label.forEach (callback)=>
8
+ callback.call(@, target)
9
+
10
+ register_setup: (label_or_callback, labelled_callback)->
11
+ unless labelled_callback?
12
+ labelled_callback = label_or_callback
13
+ label_or_callback = 'anonymous'
14
+ (@setup_callbacks[label_or_callback] ||= []).push labelled_callback
15
+
16
+ @HawkEye = new HawkEye()
17
+ Dunlop.register_setup 'hawk-eye', (target) => @HawkEye.setup(target)
@@ -0,0 +1,256 @@
1
+ class DateBinVariable
2
+ constructor: ->
3
+ @config = {}
4
+ configure: (config)->
5
+ @config = config
6
+ @setup()
7
+ setup: (data)->
8
+ @data = data if data
9
+ return unless @data
10
+ #window.data = data
11
+ @has_no_date = !!data.payload['no_date']
12
+ @container = $(data.target).html('')
13
+ @min_date = moment(@data.min_date)
14
+ @max_date = moment(@data.max_date)
15
+ @bin_type = data.bin_type
16
+ @date_key_format = "YYYY-MM-DD"
17
+ @active_facets = []
18
+
19
+ @generate_bar_chart()
20
+ @generate_facet_charts()
21
+
22
+
23
+ generate_bar_chart: ->
24
+ @bar_chart_container = $('<div></div>').addClass('bar-chart').appendTo(@container)
25
+ @set_bar_chart_keys()
26
+ columns = []
27
+ for variable in @data.variables
28
+ columns.push @get_variable_column(variable)
29
+ columns.unshift @bar_chart_ticks()
30
+
31
+ # Activating focus date helpers
32
+ number_of_ticks = columns[0].length - 1
33
+ number_of_variables = @data.variables.length
34
+
35
+ @bar_chart = c3.generate
36
+ bindto: @bar_chart_container.get(0)
37
+ data:
38
+ x: 'x'
39
+ columns: columns
40
+ type: 'bar'
41
+ #onmouseover: (d) => @recalculate_facets(date: d.x, variable: d.id)
42
+ #onmouseout: => @recalculate_facets()
43
+ onclick: (d, path)=>
44
+ return if @active_facets.length
45
+ `var graph=this`
46
+ color_def = {}
47
+ date_key = @bar_chart_keys[d.index]
48
+
49
+ if @focus_date is date_key
50
+ @focus_date = null
51
+ graph.data.colors @original_bar_chart_colors
52
+ else
53
+ #color_def[d.id] = "#aaaaaa"
54
+ for var_name in Object.keys(graph.data.colors())
55
+ color_def[var_name] = "#aaaaaa"
56
+
57
+ graph.data.colors(color_def)
58
+ # ugly solution to manually color the svg paths another color after render
59
+ delay 500, ->
60
+ bar_paths = $(graph.element).find('path.c3-bar')
61
+ for ivar in [0...number_of_variables]
62
+ bar_paths.get(d.x + ivar*number_of_ticks).style.fill = "rgb(200,100,100)"
63
+ @focus_date = date_key
64
+ @recalculate_facets(date: @focus_date)
65
+ axis:
66
+ x:
67
+ type: 'category'
68
+ @original_bar_chart_colors = $.extend({}, @bar_chart.data.colors())
69
+
70
+ reload_bar_chart: ->
71
+ columns = []
72
+ for variable in @data.variables
73
+ columns.push @get_variable_column(variable)
74
+ #columns.unshift @bar_chart_ticks()
75
+ @bar_chart.load
76
+ columns: columns
77
+
78
+ recalculate_facets: (options = {})->
79
+ for variable in @data.variables
80
+ #continue if options.variable and (options.variable is variable.name or options.variable is variable.label)
81
+ for facet, facet_index in @data.facets
82
+ facet_columns = []
83
+ for facet_value in facet.values
84
+ facet_total = @get_facet_total(variable.name, facet_index, facet_value, options)
85
+ facet_columns.push [facet_value, facet_total]
86
+ @facets[variable.name][facet.name].chart.load(columns: facet_columns)
87
+ @set_facet_legend_rows @facets[variable.name][facet.name].container.find('tbody'), facet_columns
88
+
89
+ generate_facet_charts: ->
90
+ @facets = {}
91
+ for variable in @data.variables
92
+ @facets[variable.name] = {}
93
+ for facet, facet_index in @data.facets
94
+ facet_container = $('<div></div>').addClass('facet-container').addClass(facet.name).data('variable', variable.name).data('facet', facet.name).appendTo(@container)
95
+ facet_graph_container = $('<div></div>').addClass('facet-graph').addClass(facet.name).appendTo(facet_container)
96
+ facet_legend = $("<div><table><thead><tr><th colspan='99'>#{facet.label}</th></tr></thead><tbody></tbody></table></div>").addClass('facet-legend').addClass(facet.name).addClass(variable.name).appendTo(facet_container)
97
+ facet_legend_body = facet_legend.find('tbody')
98
+ facet_container.append()
99
+ @facets[variable.name][facet.name] = {container: facet_container, index: facet_index}
100
+ facet_columns = []
101
+ for facet_value in facet.values
102
+ facet_columns.push [facet_value, @get_facet_total(variable.name, facet_index, facet_value)]
103
+ @set_facet_legend_rows(facet_legend_body, facet_columns)
104
+ do (variable, facet, facet_index) => # lock iteration closure
105
+ graph_settings =
106
+ bindto: facet_graph_container.get(0)
107
+ data:
108
+ columns: facet_columns
109
+ type: 'pie'
110
+ onclick: (d)=>
111
+ return if @focus_date
112
+ @set_active_facet variable, facet, d.id
113
+ true
114
+ pie:
115
+ label:
116
+ format: (value, ratio, id) -> value
117
+ legend:
118
+ item:
119
+ onclick: (facet_value)=>
120
+ @set_active_facet(variable, facet, facet_value)
121
+ true
122
+ #format: (value, ratio, id) -> "#{value} #{d3.format('%')(ratio)}"
123
+ if colors = @config.facet?.colors?[facet.name]
124
+ graph_settings.data.colors = colors
125
+ @facets[variable.name][facet.name].chart = c3.generate graph_settings
126
+
127
+ set_active_facet: (variable, facet, facet_value) ->
128
+ facet_css_class = facet.name
129
+ $(".facet-container.#{facet_css_class} .facet-legend tr").removeClass('facet-active')
130
+ $(".facet-container.#{facet_css_class} .c3-chart-arc").removeClass("facet-active").removeClass("facet-inactive")
131
+ facet_already_active = false
132
+ for active_facet in @active_facets
133
+ if active_facet.name is facet.name
134
+ facet_already_active = true
135
+ if active_facet.values[0] is facet_value # second click on same face value removes the facet lock
136
+ @active_facets = @active_facets.filter( (active_facet) -> active_facet.name isnt facet.name )
137
+ else # other facet value is chosen
138
+ active_facet.values = [facet_value]
139
+ $(".facet-container.#{facet_css_class} .facet-legend tr.legend-var-#{facet_value.dasherize()}").addClass('facet-active')
140
+ $(".facet-container.#{facet_css_class} .c3-chart-arc").addClass('facet-inactive')
141
+ $(".facet-container.#{facet_css_class} .c3-chart-arc.c3-target-#{facet_value.dasherize()}").removeClass("facet-inactive").addClass('facet-active')
142
+ else
143
+ # limit other facets
144
+ unless facet_already_active
145
+ @active_facets.push
146
+ name: facet.name
147
+ label: facet.label
148
+ values: [facet_value]
149
+ variable: variable.name
150
+ index: @facets[variable.name][facet.name].index
151
+ $(".facet-container.#{facet_css_class} .c3-chart-arc").addClass('facet-inactive')
152
+ $(".facet-container.#{facet_css_class} .c3-chart-arc.c3-target-#{facet_value.dasherize()}").removeClass("facet-inactive").addClass('facet-active')
153
+ $(".facet-container.#{facet_css_class} .facet-legend tr.legend-var-#{facet_value.dasherize()}").addClass('facet-active')
154
+ @reload_bar_chart()
155
+
156
+ set_facet_legend_rows: (tbody, rows)->
157
+ total = rows.reduce( ((t, r)-> t + r[1]), 0)
158
+ tbody.html('')
159
+ for row in rows
160
+ html_row = $('<tr></tr>').addClass("legend-var-#{String(row[0]).dasherize()}")
161
+ html_row.append $('<td></td>').text row[0]
162
+ html_row.append $('<td></td>').text row[1]
163
+ html_row.append $('<td></td>').text d3.format('%')(row[1]/total)
164
+ tbody.append html_row
165
+ tbody
166
+
167
+ get_facet_total: (variable, facet_index, facet_value, options = {})->
168
+ return @get_facet_total_for_date(options.date, variable, facet_index, facet_value) if options.date
169
+ total = 0
170
+ for date, results of @data.payload
171
+ total += @get_facet_total_for_date(date, variable, facet_index, facet_value, results)
172
+ total
173
+
174
+ get_facet_total_for_date: (date, variable, facet_index, facet_value, results)->
175
+ results ||= @data.payload[date]
176
+ results ||= []
177
+ total = 0
178
+ for result in results
179
+ total += result[variable] if result[variable] and result.facets[facet_index] is facet_value
180
+ total
181
+
182
+ get_variable_column: (variable)->
183
+ ary = [variable.label]
184
+ for bar_chart_key in @bar_chart_keys
185
+ ary.push @get_variable_total(variable.name, bar_chart_key)
186
+ ary
187
+
188
+ set_bar_chart_keys: ->
189
+ @bar_chart_keys = []
190
+ if @has_no_date
191
+ @bar_chart_keys.push 'no_date'
192
+ date_key = @min_date.format(@date_key_format)
193
+ @bar_chart_keys.push date_key
194
+ next_date = moment(@min_date).add(1, @bin_type)
195
+ while next_date <= @max_date
196
+ date_key = next_date.format(@date_key_format)
197
+ @bar_chart_keys.push date_key
198
+ next_date.add 1, @bin_type
199
+ @bar_chart_keys
200
+
201
+ get_variable_total: (variable, num)->
202
+ return 0 unless result_parts = @data.payload[num]
203
+ if @active_facets.length
204
+ total = 0
205
+ for result_part in result_parts
206
+ # do not add the value to the total if the facet value is not present in the result part
207
+ should_be_added_to_total = true
208
+ for active_facet in @active_facets
209
+ should_be_added_to_total = false unless active_facet.values.indexOf(result_part.facets[active_facet.index]) > -1
210
+ total += result_part[variable] if should_be_added_to_total
211
+ return total
212
+ else
213
+ return result_parts.map((part)->part[variable]).reduce (t, v) -> t + v
214
+
215
+ bar_chart_ticks: ->
216
+ x_row = ['x'] # tick below the bar
217
+ start_index = 0
218
+ if @has_no_date
219
+ x_row.push 'x'
220
+ start_index += 1 # first key will be 'no_date'
221
+ for key in @bar_chart_keys.slice(start_index)
222
+ x_row.push @format_bar_tick(key)
223
+ #x_row = x_row.concat ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
224
+ x_row
225
+
226
+ format_bar_tick: (key)->
227
+ date = moment(key)
228
+ format = "YYYY-MM-DD"
229
+ switch @bin_type
230
+ when "quarter" then return "#{date.quarter()}/#{date.year()}"
231
+ when "month"
232
+ format = "MMM"
233
+ format += " YY" if @min_date.year() isnt @max_date.year()
234
+ when "week"
235
+ result = "#{date.week()}"
236
+ result += "/#{date.year()}" if @min_date.year() isnt @max_date.year()
237
+ return result
238
+
239
+ date.format format
240
+ HawkEye.DateBinVariable = new DateBinVariable()
241
+ HawkEye.DateBinVariable.configure
242
+ facet:
243
+ colors:
244
+ state:
245
+ uncategorized: "#eee"
246
+ unplanned: "#aaa"
247
+ planned: "#aaa"
248
+ active: "#55f"
249
+ overdue: "#ffa500"
250
+ any_rejected: "#e44"
251
+ all_completed: "#5b5"
252
+ # workflow_step - duplicates
253
+ pending: "#eee"
254
+ processing: "#55f"
255
+ rejected: "#e44"
256
+ completed: "#5b5"
@@ -0,0 +1,256 @@
1
+ @dashboard = (id, fData) ->
2
+ barColor = 'steelblue'
3
+ # compute total for each state.
4
+
5
+ segColor = (c) ->
6
+ {
7
+ stateO: '#807dba'
8
+ stateP: '#e08214'
9
+ stateT: '#41ab5d'
10
+ }[c]
11
+
12
+ # function to handle histogram.
13
+
14
+ histoGram = (fD) ->
15
+ hG = {}
16
+ hGDim =
17
+ t: 60
18
+ r: 0
19
+ b: 30
20
+ l: 0
21
+
22
+ mouseover = (d) ->
23
+ # utility function to be called on mouseover.
24
+ # filter for selected state.
25
+ st = fData.filter((s) ->
26
+ s.State == d[0]
27
+ )[0]
28
+ nD = d3.keys(st.freq).map((s) ->
29
+ {
30
+ type: s
31
+ freq: st.freq[s]
32
+ }
33
+ )
34
+ # call update functions of pie-chart and legend.
35
+ pC.update nD
36
+ leg.update nD
37
+ return
38
+
39
+ mouseout = (d) ->
40
+ # utility function to be called on mouseout.
41
+ # reset the pie-chart and legend
42
+ pC.update tF
43
+ leg.update tF
44
+ return
45
+
46
+ hGDim.w = 500 - (hGDim.l) - (hGDim.r)
47
+ hGDim.h = 300 - (hGDim.t) - (hGDim.b)
48
+ #create svg for histogram.
49
+ hGsvg = d3.select(id).append('svg').attr('width', hGDim.w + hGDim.l + hGDim.r).attr('height', hGDim.h + hGDim.t + hGDim.b + 100).append('g').attr('transform', 'translate(' + hGDim.l + ',' + hGDim.t + ')')
50
+ # create function for x-axis mapping.
51
+ x = d3.scale.ordinal().rangeRoundBands([
52
+ 0
53
+ hGDim.w
54
+ ], 0.1).domain(fD.map((d) ->
55
+ d[0]
56
+ ))
57
+ # Add x-axis to the histogram svg.
58
+ hGsvg.append('g').attr('class', 'x axis').attr('transform', 'translate(0,' + hGDim.h + ')')
59
+ .call(d3.svg.axis().scale(x).orient('bottom'))
60
+ .selectAll('text')
61
+ .style("text-anchor", "end")
62
+ .attr("dx", "-.8em")
63
+ .attr("dy", ".15em")
64
+ .attr("transform", "rotate(-65)" )
65
+ # Create function for y-axis map.
66
+ y = d3.scale.linear().range([
67
+ hGDim.h
68
+ 0
69
+ ]).domain([
70
+ 0
71
+ d3.max(fD, (d) ->
72
+ d[1]
73
+ )
74
+ ])
75
+ # Create bars for histogram to contain rectangles and freq labels.
76
+ bars = hGsvg.selectAll('.bar').data(fD).enter().append('g').attr('class', 'bar')
77
+ #create the rectangles.
78
+ bars.append('rect').attr('x', (d) ->
79
+ x d[0]
80
+ ).attr('y', (d) ->
81
+ y d[1]
82
+ ).attr('width', x.rangeBand()).attr('height', (d) ->
83
+ hGDim.h - y(d[1])
84
+ ).attr('fill', barColor).on('mouseover', mouseover).on 'mouseout', mouseout
85
+ # mouseout is defined below.
86
+ #Create the frequency labels above the rectangles.
87
+ bars.append('text').text((d) ->
88
+ d3.format(',') d[1]
89
+ ).attr('x', (d) ->
90
+ x(d[0]) + x.rangeBand() / 2
91
+ ).attr('y', (d) ->
92
+ y(d[1]) - 5
93
+ ).attr 'text-anchor', 'middle'
94
+ # create function to update the bars. This will be used by pie-chart.
95
+
96
+ hG.update = (nD, color) ->
97
+ # update the domain of the y-axis map to reflect change in frequencies.
98
+ y.domain [
99
+ 0
100
+ d3.max(nD, (d) ->
101
+ `var bars`
102
+ d[1]
103
+ )
104
+ ]
105
+ # Attach the new data to the bars.
106
+ bars = hGsvg.selectAll('.bar').data(nD)
107
+ # transition the height and color of rectangles.
108
+ bars.select('rect').transition().duration(500).attr('y', (d) ->
109
+ y d[1]
110
+ ).attr('height', (d) ->
111
+ hGDim.h - y(d[1])
112
+ ).attr 'fill', color
113
+ # transition the frequency labels location and change value.
114
+ bars.select('text').transition().duration(500).text((d) ->
115
+ d3.format(',') d[1]
116
+ ).attr 'y', (d) ->
117
+ y(d[1]) - 5
118
+ return
119
+
120
+ hG
121
+
122
+ # function to handle pieChart.
123
+
124
+ pieChart = (pD) ->
125
+ pC = {}
126
+ pieDim =
127
+ w: 250
128
+ h: 250
129
+ # Utility function to be called on mouseover a pie slice.
130
+
131
+ mouseover = (d) ->
132
+ # call the update function of histogram with new data.
133
+ hG.update fData.map((v) ->
134
+ [
135
+ v.State
136
+ v.freq[d.data.type]
137
+ ]
138
+ ), segColor(d.data.type)
139
+ return
140
+
141
+ #Utility function to be called on mouseout a pie slice.
142
+
143
+ mouseout = (d) ->
144
+ # call the update function of histogram with all data.
145
+ hG.update fData.map((v) ->
146
+ [
147
+ v.State
148
+ v.total
149
+ ]
150
+ ), barColor
151
+ return
152
+
153
+ # Animating the pie-slice requiring a custom function which specifies
154
+ # how the intermediate paths should be drawn.
155
+
156
+ arcTween = (a) ->
157
+ i = d3.interpolate(@_current, a)
158
+ @_current = i(0)
159
+ (t) ->
160
+ arc i(t)
161
+
162
+ pieDim.r = Math.min(pieDim.w, pieDim.h) / 2
163
+ # create svg for pie chart.
164
+ piesvg = d3.select(id).append('svg').attr('width', pieDim.w).attr('height', pieDim.h).append('g').attr('transform', 'translate(' + pieDim.w / 2 + ',' + pieDim.h / 2 + ')')
165
+ # create function to draw the arcs of the pie slices.
166
+ arc = d3.svg.arc().outerRadius(pieDim.r - 10).innerRadius(0)
167
+ # create a function to compute the pie slice angles.
168
+ pie = d3.layout.pie().sort(null).value((d) ->
169
+ d.freq
170
+ )
171
+ # Draw the pie slices.
172
+ piesvg.selectAll('path').data(pie(pD)).enter().append('path').attr('d', arc).each((d) ->
173
+ @_current = d
174
+ return
175
+ ).style('fill', (d) ->
176
+ segColor d.data.type
177
+ ).on('mouseover', mouseover).on 'mouseout', mouseout
178
+ # create function to update pie-chart. This will be used by histogram.
179
+
180
+ pC.update = (nD) ->
181
+ piesvg.selectAll('path').data(pie(nD)).transition().duration(500).attrTween 'd', arcTween
182
+ return
183
+
184
+ pC
185
+
186
+ # function to handle legend.
187
+
188
+ legend = (lD) ->
189
+ `var legend`
190
+ leg = {}
191
+ # create table for legend.
192
+ legend = d3.select(id).append('table').attr('class', 'legend')
193
+ # create one row per segment.
194
+ tr = legend.append('tbody').selectAll('tr').data(lD).enter().append('tr')
195
+ # create the first column for each segment.
196
+
197
+ getLegend = (d, aD) ->
198
+ # Utility function to compute percentage.
199
+ d3.format('%') d.freq / d3.sum(aD.map((v) ->
200
+ v.freq
201
+ ))
202
+
203
+ tr.append('td').append('svg').attr('width', '16').attr('height', '16').append('rect').attr('width', '16').attr('height', '16').attr 'fill', (d) ->
204
+ segColor d.type
205
+ # create the second column for each segment.
206
+ tr.append('td').text (d) ->
207
+ d.type
208
+ # create the third column for each segment.
209
+ tr.append('td').attr('class', 'legendFreq').text (d) ->
210
+ d3.format(',') d.freq
211
+ # create the fourth column for each segment.
212
+ tr.append('td').attr('class', 'legendPerc').text (d) ->
213
+ getLegend d, lD
214
+ # Utility function to be used to update the legend.
215
+
216
+ leg.update = (nD) ->
217
+ # update the data attached to the row elements.
218
+ l = legend.select('tbody').selectAll('tr').data(nD)
219
+ # update the frequencies.
220
+ l.select('.legendFreq').text (d) ->
221
+ d3.format(',') d.freq
222
+ # update the percentage column.
223
+ l.select('.legendPerc').text (d) ->
224
+ getLegend d, nD
225
+ return
226
+
227
+ leg
228
+
229
+ fData.forEach (d) ->
230
+ d.total = d.freq.stateO + d.freq.stateP + d.freq.stateT
231
+ return
232
+ # calculate total frequency by segment for all state.
233
+ tF = [
234
+ 'stateO'
235
+ 'stateP'
236
+ 'stateT'
237
+ ].map((d) ->
238
+ {
239
+ type: d
240
+ freq: d3.sum(fData.map((t) ->
241
+ t.freq[d]
242
+ ))
243
+ }
244
+ )
245
+ # calculate total frequency by state for all segment.
246
+ sF = fData.map((d) ->
247
+ [
248
+ d.State
249
+ d.total
250
+ ]
251
+ )
252
+ hG = histoGram(sF)
253
+ pC = pieChart(tF)
254
+ leg = legend(tF)
255
+ # create the legend.
256
+ return