i_wonder 0.0.1 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +1 -24
- data/app/assets/javascripts/i_wonder/application.js +1 -0
- data/app/assets/javascripts/i_wonder/reports.js +11 -1
- data/app/assets/stylesheets/i_wonder/_shared.css.scss +6 -0
- data/app/assets/stylesheets/i_wonder/application.css.scss +2 -5
- data/app/assets/stylesheets/i_wonder/reports.css.scss +7 -0
- data/app/controllers/i_wonder/reports_controller.rb +36 -3
- data/app/models/i_wonder/event.rb +16 -6
- data/app/models/i_wonder/metric.rb +57 -14
- data/app/models/i_wonder/report.rb +2 -2
- data/app/models/i_wonder/snapshot.rb +2 -1
- data/app/views/i_wonder/events/index.html.erb +4 -0
- data/app/views/i_wonder/metrics/_options_for_custom.html.erb +2 -2
- data/app/views/i_wonder/metrics/_options_for_model_counter.html.erb +2 -2
- data/app/views/i_wonder/metrics/index.html.erb +3 -1
- data/app/views/i_wonder/reports/_form.html.erb +1 -1
- data/app/views/i_wonder/reports/_line_report.html.erb +10 -33
- data/app/views/i_wonder/reports/_line_report.js.erb +20 -0
- data/app/views/layouts/i_wonder.html.erb +4 -0
- data/db/migrate/20111022230720_i_wonder_migrations.rb +5 -0
- data/lib/i_wonder/configuration.rb +3 -3
- data/lib/i_wonder/logging/middleware.rb +5 -4
- data/lib/i_wonder/version.rb +1 -1
- data/lib/tasks/i_wonder_tasks.rake +4 -0
- data/test/dummy/app/assets/javascripts/application.js +1 -0
- data/test/dummy/db/schema.rb +5 -0
- data/test/dummy/log/development.log +1941 -0
- data/test/dummy/log/test.log +25698 -0
- data/test/dummy/tmp/cache/assets/D8E/250/sprockets%2Fefe57e932e1d26c3b6af42a5311f0a1c +0 -0
- data/test/dummy/tmp/cache/assets/DA7/BA0/sprockets%2F463a9dd811f2418d96fa72cfe05fb8cd +0 -0
- data/test/integration/i_wonder/reports_controller_test.rb +3 -3
- data/test/unit/i_wonder/event_test.rb +25 -0
- data/test/unit/i_wonder/metric_collection_test.rb +53 -29
- data/test/unit/i_wonder/metric_creation_test.rb +9 -7
- data/test/unit/i_wonder/report_test.rb +9 -9
- metadata +22 -23
- data/test/dummy/tmp/pids/server.pid +0 -1
data/README.rdoc
CHANGED
@@ -54,29 +54,6 @@ Add the engine to your routes. This is where the dashboard will be hosted
|
|
54
54
|
mount IWonder::Engine => "/super_analytics_engine"
|
55
55
|
end
|
56
56
|
|
57
|
-
This engine will use your standard application layout (unless you set a different layout in the configurations explained later). Whichever layout you use, you
|
58
|
-
must include a yield in the HTML header so the CSS and Javascript can be loaded.
|
59
|
-
|
60
|
-
<%= yield :extra_scripts_and_styles %>
|
61
|
-
|
62
|
-
This engine also assumes that your layout will handle the flash notifications.
|
63
|
-
|
64
|
-
===== Example application layout:
|
65
|
-
|
66
|
-
<html>
|
67
|
-
<head>
|
68
|
-
|
69
|
-
... your javascript/css/meta/header stuff here ...
|
70
|
-
|
71
|
-
<%= yield :extra_scripts_and_styles %>
|
72
|
-
</head>
|
73
|
-
<body>
|
74
|
-
<%= notifications %>
|
75
|
-
<%= yield %>
|
76
|
-
</body>
|
77
|
-
</html
|
78
|
-
|
79
|
-
|
80
57
|
=== Setting up the hourly cron task
|
81
58
|
|
82
59
|
This gem hooks into the "<code>rake cron</code>" request. It can't take snapshots of your metrics any faster than this cron is run. We recommend every hour.
|
@@ -103,7 +80,7 @@ You can choose to navigate directly to the dashboard. If you want to include a l
|
|
103
80
|
|
104
81
|
If you want to configure this gem, add this to an initializer.
|
105
82
|
|
106
|
-
IWonder.
|
83
|
+
IWonder.configure do |c|
|
107
84
|
c.controllers_to_ignore = [] # These are all the controllers which don't log any events (i_wonder is also ignored)
|
108
85
|
c.only_log_hits_on_200 = true # By default hits and new visitors won't be logged on anything but a 200 or 304 (not modified). Your custom events will still be recorded.
|
109
86
|
c.back_to_app_link = "/" # this is the link hitting the home button in IWonder will take you to.
|
@@ -1,4 +1,14 @@
|
|
1
1
|
// Place all the behaviors and hooks related to the matching controller here.
|
2
2
|
// All this logic will automatically be available in application.js.
|
3
|
+
//= require highcharts
|
3
4
|
//= require_self
|
4
|
-
|
5
|
+
|
6
|
+
|
7
|
+
$(function(){
|
8
|
+
$("#chart_holder .calendar").datepicker({ dateFormat: 'yy/mm/dd' });
|
9
|
+
$("#ui-datepicker-div").hide(); // this shouldn't be needed, but there seems to be a bug
|
10
|
+
|
11
|
+
$("#chart_holder #chart_options input").change(function(){
|
12
|
+
$(this).closest("form").submit();
|
13
|
+
});
|
14
|
+
});
|
@@ -13,6 +13,12 @@ $pale_red: #ffd2d2;
|
|
13
13
|
box-shadow:$x_offset $y_offset $size $color;
|
14
14
|
}
|
15
15
|
|
16
|
+
@mixin inset_shadow($x_offset: 0px, $y_offset: 1px, $size: 8px, $color: rgba(0, 0, 0, 0.5)) {
|
17
|
+
-webkit-box-shadow:$x_offset $y_offset $size $color inset;
|
18
|
+
-moz-box-shadow:$x_offset $y_offset $size $color inset;
|
19
|
+
box-shadow:$x_offset $y_offset $size $color inset;
|
20
|
+
}
|
21
|
+
|
16
22
|
@mixin gradiant ($color_a:#aaa, $color_b:#bbb) {
|
17
23
|
position:relative;
|
18
24
|
background-image: -webkit-linear-gradient(top, $color_a, $color_b); /* Chrome 10+, Saf5.1+ */
|
@@ -30,6 +30,8 @@ html, body { background-color: #e5e5e5; font-family: Verdana, Helvetica, Arial;
|
|
30
30
|
}
|
31
31
|
#container { padding: 20px 40px; }
|
32
32
|
|
33
|
+
p.description { font-size:90%; color:#555;}
|
34
|
+
|
33
35
|
p.empty_list {color:#666; }
|
34
36
|
|
35
37
|
ul.primary_list { padding:0px; margin:10px 0; border:1px solid $light_blue_box_border; border-bottom:none; background-color:$light_blue_box_background;
|
@@ -45,11 +47,6 @@ html, body { background-color: #e5e5e5; font-family: Verdana, Helvetica, Arial;
|
|
45
47
|
}
|
46
48
|
}
|
47
49
|
}
|
48
|
-
|
49
|
-
.show {
|
50
|
-
p.description { font-size:90%; color:#555;}
|
51
|
-
}
|
52
|
-
|
53
50
|
|
54
51
|
form.main_form { background-color:$light_blue_box_background; border:1px solid $light_blue_box_border; padding:10px 15px; display:inline-block;
|
55
52
|
h3 {margin:0 0 10px 0;}
|
@@ -16,4 +16,11 @@ form.report {
|
|
16
16
|
.report_metric_ids { padding:0px; margin:10px 0;
|
17
17
|
li {padding:0px; margin:0px; list-style:none;}
|
18
18
|
}
|
19
|
+
}
|
20
|
+
|
21
|
+
#chart_holder {background-color:#f0f1fa; padding:8px; border:1px solid #ddd; @include rounded_corners(4px); @include inset_shadow(1px, 1px, 3px, rgba(0,0,0,0.3));
|
22
|
+
#chart_options {
|
23
|
+
label {color:#555; font-size:90%; font-weight:bold;}
|
24
|
+
}
|
25
|
+
#iw_chart_container {background-color:#fff; margin-top:5px; border:1px solid #ddd; @include shadow(1px, 1px, 3px, rgba(0,0,0,0.3));}
|
19
26
|
}
|
@@ -9,9 +9,20 @@ module IWonder
|
|
9
9
|
def show
|
10
10
|
@report = Report.find(params[:id])
|
11
11
|
|
12
|
-
@start_time = Time.zone.
|
13
|
-
@end_time = (params[:end_time]
|
14
|
-
@interval_length =
|
12
|
+
@start_time = (params[:start_time] ? Time.zone.parse(params[:start_time]) : Time.zone.now - 1.month)
|
13
|
+
@end_time = (params[:end_time] ? Time.zone.parse(params[:end_time]) : Time.zone.now)
|
14
|
+
@interval_length = default_interval_length
|
15
|
+
|
16
|
+
respond_to {|format|
|
17
|
+
format.html { }
|
18
|
+
format.js {
|
19
|
+
if @report.line?
|
20
|
+
render :partial => "line_report.js"
|
21
|
+
else
|
22
|
+
render :text => "?"
|
23
|
+
end
|
24
|
+
}
|
25
|
+
}
|
15
26
|
end
|
16
27
|
|
17
28
|
def new
|
@@ -49,5 +60,27 @@ module IWonder
|
|
49
60
|
redirect_to reports_path, :notice => "Report has been destroyed"
|
50
61
|
end
|
51
62
|
|
63
|
+
protected
|
64
|
+
|
65
|
+
def default_interval_length
|
66
|
+
length = @end_time - @start_time
|
67
|
+
|
68
|
+
if length > 2.months
|
69
|
+
interval_length = 1.week
|
70
|
+
elsif length > 1.week
|
71
|
+
interval_length = 1.days
|
72
|
+
else
|
73
|
+
interval_length = 1.hours
|
74
|
+
end
|
75
|
+
|
76
|
+
# can't show a smaller frequency than the snampshots get taken in.
|
77
|
+
longest_metric_frequency = @report.metrics.collect(&:frequency).max
|
78
|
+
if longest_metric_frequency > interval_length
|
79
|
+
interval_length = longest_metric_frequency
|
80
|
+
end
|
81
|
+
|
82
|
+
return interval_length
|
83
|
+
end
|
84
|
+
|
52
85
|
end
|
53
86
|
end
|
@@ -7,16 +7,28 @@ module IWonder
|
|
7
7
|
|
8
8
|
class << self
|
9
9
|
def merge_session_to_user(session_id, user_id)
|
10
|
-
|
10
|
+
# grab the session_id off of the new_visitor attached to that user
|
11
|
+
new_visitor_event = Event.where(:user_id => user_id, :event_type => "new_visitor").first
|
12
|
+
original_session_id = (new_visitor_event ? new_visitor_event.session_id : session_id)
|
11
13
|
|
12
|
-
|
14
|
+
|
15
|
+
# for all events on the current session, attach the user and session from that first event
|
16
|
+
update_all({:user_id => user_id, :session_id => original_session_id}, ["session_id = ? AND user_id IS NULL", session_id])
|
17
|
+
|
18
|
+
# clear our any new_visitor events other than the first one
|
19
|
+
if new_visitor_event and original_session_id != session_id
|
20
|
+
Event.where(:user_id => user_id, :event_type => "new_visitor").where("id <> ?", new_visitor_event.id).delete_all
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
return original_session_id
|
13
25
|
end
|
14
26
|
# handle_asynchronously :merge_session_to_user
|
15
27
|
|
16
28
|
|
17
29
|
def fast_create(attr_hash)
|
18
|
-
attr_hash[:created_at] = Time.now.utc
|
19
|
-
attr_hash[:updated_at] = Time.now.utc
|
30
|
+
attr_hash[:created_at] = Time.zone.now.utc
|
31
|
+
attr_hash[:updated_at] = Time.zone.now.utc
|
20
32
|
|
21
33
|
keys = [:event_type, :account_id, :user_id, :session_id, :controller, :action, :extra_details, :remote_ip, :user_agent, :referrer, :created_at, :updated_at]
|
22
34
|
key_str = keys.collect(&:to_s).join(", ")
|
@@ -54,8 +66,6 @@ module IWonder
|
|
54
66
|
}.first
|
55
67
|
end
|
56
68
|
|
57
|
-
|
58
|
-
|
59
69
|
end
|
60
70
|
end
|
61
71
|
end
|
@@ -1,7 +1,10 @@
|
|
1
1
|
module IWonder
|
2
2
|
class Metric < ActiveRecord::Base
|
3
|
-
|
4
|
-
|
3
|
+
BACK_DATE_ITERATIONS = 30
|
4
|
+
|
5
|
+
attr_accessible :name, :frequency, :archived, :collection_method, :back_date_snapshots
|
6
|
+
attr_accessor :back_date_snapshots
|
7
|
+
attr_writer :back_date_snapshots
|
5
8
|
|
6
9
|
serialize :options, Hash
|
7
10
|
attr_accessible :collection_type, :combination_rule, :takes_snapshots
|
@@ -20,9 +23,10 @@ module IWonder
|
|
20
23
|
has_many :reports, :through => :report_memberships
|
21
24
|
has_many :snapshots do
|
22
25
|
def most_recent
|
23
|
-
order("
|
26
|
+
order("end_time DESC").first
|
24
27
|
end
|
25
28
|
end
|
29
|
+
accepts_nested_attributes_for :snapshots, :allow_destroy => true
|
26
30
|
|
27
31
|
# ==============================================================================================================================
|
28
32
|
# The following methods are for creating the metrics with the coorect values ===================================================
|
@@ -55,6 +59,14 @@ module IWonder
|
|
55
59
|
true # this preventing it from thinking the validation failed
|
56
60
|
end
|
57
61
|
|
62
|
+
before_validation :remove_existing_snapshots
|
63
|
+
def remove_existing_snapshots
|
64
|
+
if collection_method_changed? or frequency_changed?
|
65
|
+
self.earliest_measurement = nil
|
66
|
+
self.snapshots.each(&:mark_for_destruction)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
58
70
|
validate :avoid_dangerous_words
|
59
71
|
def avoid_dangerous_words
|
60
72
|
if collection_method.present? and collection_method.gsub(QUOTE_REMOVER, "") =~ DANGEROUS_WORDS
|
@@ -82,6 +94,11 @@ module IWonder
|
|
82
94
|
}
|
83
95
|
end
|
84
96
|
end
|
97
|
+
|
98
|
+
if self.model_counter_method == "Creation Rate" and !self.model_counter_class.constantize.new.respond_to?("created_at")
|
99
|
+
errors.add(:base, "Can't calculate creation rate on models without a :created_at column")
|
100
|
+
end
|
101
|
+
|
85
102
|
rescue Exception => e
|
86
103
|
errors.add(:model_counter_class, "is not a valid class")
|
87
104
|
end
|
@@ -117,10 +134,19 @@ module IWonder
|
|
117
134
|
query += model_counter_scopes.dup
|
118
135
|
end
|
119
136
|
|
137
|
+
#TODO: these queries should be scoped under the table name
|
120
138
|
if model_counter_method == "Creation Rate"
|
139
|
+
self.combination_rule = "sum"
|
121
140
|
self.collection_method = "#{query}.where(\"created_at >= ? AND created_at < ?\", start_time, end_time).count"
|
122
141
|
else # total numbers
|
123
|
-
|
142
|
+
|
143
|
+
self.combination_rule = "average"
|
144
|
+
if self.model_counter_class.constantize.new.respond_to?("created_at")
|
145
|
+
# this is more accurate for guessing backwards
|
146
|
+
self.collection_method = "#{query}.where(\"created_at < ?\", end_time).count"
|
147
|
+
else
|
148
|
+
self.collection_method = "#{query}.count"
|
149
|
+
end
|
124
150
|
end
|
125
151
|
end
|
126
152
|
|
@@ -145,7 +171,7 @@ module IWonder
|
|
145
171
|
# returns a hash with all the key values between the two times. If it has been collecting integers, the key will be the name of the metric
|
146
172
|
def value_from(start_time, end_time)
|
147
173
|
if takes_snapshots?
|
148
|
-
data = self.snapshots.where("
|
174
|
+
data = self.snapshots.where("start_time < ? AND end_time > ?", end_time, start_time).collect(&:data)
|
149
175
|
else
|
150
176
|
data = [run_collection_method_from(start_time, end_time)]
|
151
177
|
end
|
@@ -177,16 +203,31 @@ module IWonder
|
|
177
203
|
#TODO: some code to avoid overlap in snapshots needs to be added
|
178
204
|
def take_snapshot
|
179
205
|
start_time, end_time = timeframe_for_next_snapshot
|
180
|
-
|
206
|
+
while end_time <= Time.zone.now do
|
207
|
+
self.snapshots.create(:data => run_collection_method_from(start_time, end_time), :start_time => start_time, :end_time => end_time)
|
181
208
|
|
182
|
-
|
183
|
-
|
209
|
+
if self.earliest_measurement.nil? or self.earliest_measurement > start_time
|
210
|
+
self.update_attribute(:earliest_measurement, start_time)
|
211
|
+
end
|
212
|
+
|
213
|
+
start_time, end_time = timeframe_for_next_snapshot
|
184
214
|
end
|
185
215
|
end
|
186
216
|
|
187
217
|
after_save :back_date_if_chosen
|
188
218
|
def back_date_if_chosen
|
189
|
-
if @
|
219
|
+
if @back_date_snapshots and @back_date_snapshots.to_s =~ /1|true|on/i and takes_snapshots? and self.earliest_measurement.nil?
|
220
|
+
|
221
|
+
self.reload # this keeps bad variables and change states from sneaking in
|
222
|
+
|
223
|
+
start_time = Time.zone.now - BACK_DATE_ITERATIONS * frequency
|
224
|
+
end_time = start_time + frequency
|
225
|
+
start_time += 1.second
|
226
|
+
self.snapshots.create(:data => run_collection_method_from(start_time, end_time), :start_time => start_time, :end_time => end_time)
|
227
|
+
self.update_attribute(:earliest_measurement, start_time)
|
228
|
+
|
229
|
+
# not that the first snapshot is taken, running the :take_snapshot command will fill in the rest
|
230
|
+
take_snapshot
|
190
231
|
end
|
191
232
|
end
|
192
233
|
|
@@ -210,13 +251,15 @@ module IWonder
|
|
210
251
|
def timeframe_for_next_snapshot
|
211
252
|
recent = self.snapshots.most_recent
|
212
253
|
if recent.present?
|
213
|
-
|
214
|
-
|
254
|
+
start_time = recent.end_time
|
255
|
+
end_time = start_time + frequency
|
256
|
+
start_time += 1.second # this avoids overlap with the previous snapshot
|
215
257
|
else
|
216
|
-
|
217
|
-
|
258
|
+
start_time = Time.zone.now - frequency
|
259
|
+
end_time = Time.zone.now
|
218
260
|
end
|
219
|
-
|
261
|
+
|
262
|
+
[start_time, end_time]
|
220
263
|
end
|
221
264
|
|
222
265
|
end
|
@@ -67,7 +67,7 @@ module IWonder
|
|
67
67
|
master_hashes_array << master_hash_for_time_slice
|
68
68
|
|
69
69
|
time_iterator += interval_length
|
70
|
-
end while time_iterator <= end_time
|
70
|
+
end while (time_iterator + interval_length) <= end_time
|
71
71
|
|
72
72
|
# set the value to zero for any
|
73
73
|
master_hashes_array.each{|hash_for_slice_of_time|
|
@@ -77,7 +77,7 @@ module IWonder
|
|
77
77
|
}
|
78
78
|
|
79
79
|
keys_list.collect{|key|
|
80
|
-
{:name => key, :pointInterval => interval_length*1000, :data => master_hashes_array.collect{|mha| mha[key] }}
|
80
|
+
{:name => key, :pointStart => start_time.to_i * 1000, :pointInterval => interval_length*1000, :data => master_hashes_array.collect{|mha| mha[key] }}
|
81
81
|
}
|
82
82
|
end
|
83
83
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module IWonder
|
2
2
|
class Snapshot < ActiveRecord::Base
|
3
|
-
attr_accessible :data
|
3
|
+
attr_accessible :data, :start_time, :end_time
|
4
4
|
serialize :complex_data, Hash
|
5
5
|
|
6
6
|
belongs_to :metric
|
@@ -28,5 +28,6 @@ module IWonder
|
|
28
28
|
def complex?
|
29
29
|
self.complex_data.present?
|
30
30
|
end
|
31
|
+
|
31
32
|
end
|
32
33
|
end
|
@@ -28,8 +28,8 @@
|
|
28
28
|
</div>
|
29
29
|
|
30
30
|
<div class="field">
|
31
|
-
<%= f.check_box :
|
32
|
-
<%= f.label :
|
31
|
+
<%= f.check_box :back_date_snapshots%>
|
32
|
+
<%= f.label :back_date_snapshots, "Try and guess values for the last 30 snapshots?" %>
|
33
33
|
</div>
|
34
34
|
|
35
35
|
</div>
|
@@ -26,8 +26,8 @@
|
|
26
26
|
</div>
|
27
27
|
|
28
28
|
<div class="field">
|
29
|
-
<%= f.check_box :
|
30
|
-
<%= f.label :
|
29
|
+
<%= f.check_box :back_date_snapshots%>
|
30
|
+
<%= f.label :back_date_snapshots, "Try and guess values for the last 30 snapshots?" %>
|
31
31
|
</div>
|
32
32
|
|
33
33
|
</div>
|
@@ -1,5 +1,7 @@
|
|
1
1
|
<h1>Metrics</h1>
|
2
|
-
<
|
2
|
+
<p class="description">
|
3
|
+
Metrics are anything you want to track. They can be set up to monitor pretty much anything. To view the data collected, try creating a report for the metrics you wish to display.
|
4
|
+
</p>
|
3
5
|
|
4
6
|
<% if @metrics.empty? %>
|
5
7
|
<p class="empty_list">You don't have any metrics</p>
|
@@ -22,7 +22,7 @@
|
|
22
22
|
|
23
23
|
<div class="field">
|
24
24
|
<%= f.label :report_type %>
|
25
|
-
<%= f.select :report_type, {"Line" => {:value => "Line"}, "Bar" => {:disabled => true}, "
|
25
|
+
<%= f.select :report_type, {"Line" => {:value => "Line"}, "Bar" => {:disabled => true}, "Pie" => {:disabled => true}} %>
|
26
26
|
</div>
|
27
27
|
|
28
28
|
<hr/>
|
@@ -1,38 +1,15 @@
|
|
1
|
-
<div id="
|
1
|
+
<div id="chart_holder">
|
2
|
+
<%= form_tag report_path(@report), :method => :get, :remote => true, :id => "chart_options" do %>
|
3
|
+
<%= label_tag :start_time, "Range" %>
|
4
|
+
<%= text_field_tag :start_time, @start_time.strftime("%Y/%m/%d"), :size => 12, :class => "calendar" %>
|
5
|
+
-
|
6
|
+
<%= text_field_tag :end_time, @end_time.strftime("%Y/%m/%d"), :size => 12, :class => "calendar" %>
|
7
|
+
<% end %>
|
8
|
+
<div id="iw_chart_container"></div>
|
9
|
+
</div>
|
2
10
|
|
3
11
|
<script type="text/javascript" charset="utf-8">
|
4
12
|
$(function() {
|
5
|
-
|
6
|
-
options ||= {}
|
7
|
-
options[:chart] ||= {}
|
8
|
-
options[:chart][:renderTo] = "iw_chart_container";
|
9
|
-
|
10
|
-
options[:title] ||= { :text => "I wonder "+@report.name }
|
11
|
-
|
12
|
-
options[:series] = @report.collect_series_data(@start_time, @end_time, @interval_length);
|
13
|
-
|
14
|
-
# options[:chart][:defaultSeriesType] =
|
15
|
-
#
|
16
|
-
options[:credits] = false
|
17
|
-
#
|
18
|
-
options[:xAxis] = {}
|
19
|
-
# new_options[:xAxis][:categories] = options[:x_axis_categories]
|
20
|
-
options[:xAxis][:dateTimeLabelFormats] ||= { :day => '%b %e', :hour => '%b %e' }
|
21
|
-
options[:xAxis][:type] ||= 'datetime'
|
22
|
-
#
|
23
|
-
# new_options[:series] = options[:series]
|
24
|
-
#
|
25
|
-
# new_options[:yAxis] = (options[:yAxis] || {})
|
26
|
-
# new_options[:yAxis][:allow_decimals] = (options[:y_axis_allow_decimals] ? 'true' : 'false')
|
27
|
-
# new_options[:yAxis][:min] = (options[:y_axis_min] || 0)
|
28
|
-
# new_options[:yAxis][:title] = { :text => options[:y_axis_title] } if options[:y_axis_title].present?
|
29
|
-
#
|
30
|
-
# new_options[:legend] = (options[:legend] || { :enabled => false })
|
31
|
-
#
|
32
|
-
|
33
|
-
#
|
34
|
-
# new_options[:tooltip] = options[:tooltip] if options[:tooltip].present?
|
35
|
-
%>
|
36
|
-
var chart = new Highcharts.Chart(<%= raw options.to_json %>);
|
13
|
+
<%= render :partial => "line_report.js" %>
|
37
14
|
});
|
38
15
|
</script>
|