hawk-eye 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +1 -0
- data/README.md +2 -0
- data/Rakefile +26 -0
- data/app/assets/javascripts/hawk-eye.js.coffee +2 -0
- data/app/assets/javascripts/hawk-eye/base.js.coffee +17 -0
- data/app/assets/javascripts/hawk-eye/date-bin-variable.js.coffee +256 -0
- data/app/assets/javascripts/hawk-eye/test.js.coffee +256 -0
- data/app/assets/stylesheets/hawk-eye/graphs/_variable_bin.sass +18 -0
- data/app/assets/stylesheets/hawk-eye/hawk-eye.sass +2 -0
- data/app/assets/stylesheets/hawk-eye/test.sass +40 -0
- data/app/assets/stylesheets/hawk_eye/application.css +15 -0
- data/app/controllers/hawk_eye/application_controller.rb +14 -0
- data/app/helpers/hawk_eye/application_helper.rb +4 -0
- data/app/services/hawk_eye/graph/date_bin_helpers.rb +7 -0
- data/app/services/hawk_eye/graph/date_bin_variable.rb +89 -0
- data/app/views/hawk_eye/application/home.html.slim +15 -0
- data/app/views/layouts/hawk_eye/application.html.erb +14 -0
- data/config/routes.rb +3 -0
- data/lib/hawk-eye.rb +1 -0
- data/lib/hawk_eye.rb +7 -0
- data/lib/hawk_eye/engine.rb +5 -0
- data/lib/hawk_eye/version.rb +3 -0
- data/lib/tasks/hawk_eye_tasks.rake +4 -0
- data/spec/dummy/README.rdoc +28 -0
- data/spec/dummy/Rakefile +6 -0
- data/spec/dummy/app/assets/javascripts/application.js +13 -0
- data/spec/dummy/app/assets/stylesheets/application.css +15 -0
- data/spec/dummy/app/controllers/application_controller.rb +5 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/bin/bundle +3 -0
- data/spec/dummy/bin/rails +4 -0
- data/spec/dummy/bin/rake +4 -0
- data/spec/dummy/bin/setup +29 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/config/application.rb +32 -0
- data/spec/dummy/config/boot.rb +5 -0
- data/spec/dummy/config/database.yml +25 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +41 -0
- data/spec/dummy/config/environments/production.rb +79 -0
- data/spec/dummy/config/environments/test.rb +42 -0
- data/spec/dummy/config/initializers/assets.rb +11 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
- data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/spec/dummy/config/initializers/inflections.rb +16 -0
- data/spec/dummy/config/initializers/mime_types.rb +4 -0
- data/spec/dummy/config/initializers/session_store.rb +3 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +23 -0
- data/spec/dummy/config/routes.rb +4 -0
- data/spec/dummy/config/secrets.yml +22 -0
- data/spec/dummy/public/404.html +67 -0
- data/spec/dummy/public/422.html +67 -0
- data/spec/dummy/public/500.html +66 -0
- data/spec/dummy/public/favicon.ico +0 -0
- metadata +247 -0
checksums.yaml
ADDED
@@ -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
|
data/README.md
ADDED
data/Rakefile
ADDED
@@ -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,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
|