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.
- 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
|