twistage_stats_reports 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,139 @@
1
+ require 'stats/tzinfo_extensions'
2
+
3
+ class Stats::BreakdownReport < Stats::EventReport
4
+ set_attributes [ :id, :date, :start_date, :end_date, :title, :label, :name, :query_time, :bytes_uploaded, :bytes_encoded, :bytes_uploaded_gb, :bytes_encoded_gb, :container ]
5
+ set_numeric_attributes [ :assets_uploaded, :assets_encoded, :duration_uploaded, :duration_encoded, :duration_uploaded_min, :duration_encoded_min ]
6
+
7
+ def initialize(options = {})
8
+ @label = options[:label]
9
+
10
+ @container = options[:container]
11
+
12
+ @assets_uploaded = options[:assets_uploaded] || 0
13
+ @assets_encoded = options[:assets_encoded] || 0
14
+
15
+ @query_time = nil
16
+
17
+ @duration_uploaded = options[:duration_uploaded].to_f || 0
18
+ @duration_uploaded_min = ('%.2f' % (@duration_uploaded / 60.seconds.to_f)).to_f
19
+
20
+ @duration_encoded = options[:duration_encoded].to_f || 0
21
+ @duration_encoded_min = ('%.2f' % (@duration_encoded / 60.seconds.to_f)).to_f
22
+ end
23
+
24
+ def self.csv_keys
25
+ [ :container, :assets_encoded, :assets_uploaded, :duration_uploaded, :duration_encoded ]
26
+ end
27
+
28
+ def to_csv(options = {})
29
+ the_csv_keys = strip_keys_by_label(self.class.csv_keys, @label)
30
+ the_csv_keys.map do |k|
31
+ self.class.format_csv_value(send(k))
32
+ end.join(',')
33
+ end
34
+
35
+ def to_xml options = {}
36
+ xml = options[:builder] ||= Builder::XmlMarkup.new(:indent => options[:indent])
37
+ xml.instruct! unless options[:skip_instruct]
38
+
39
+ xml.__send__(@label) do
40
+ xml.container @container
41
+ xml.assets_encoded @assets_encoded
42
+ xml.assets_uploaded @assets_uploaded
43
+ xml.duration_encoded @duration_encoded
44
+ xml.duration_uploaded @duration_uploaded
45
+ end
46
+ end
47
+
48
+ def to_json(options = {})
49
+ report = HashWithIndifferentAccess.new
50
+ report['container'] = @container
51
+ report['assets_encoded'] = @assets_encoded
52
+ report['assets_uploaded'] = @assets_uploaded
53
+ report['duration_encoded'] = @duration_encoded
54
+ report['duration_uploaded'] = @duration_uploaded
55
+
56
+ report.to_json
57
+ end
58
+
59
+ def self.to_amcharts(reports, show_date, show_date_range, options = {})
60
+ assets_encoded = []
61
+ assets_uploaded = []
62
+ complete_labels = []
63
+ duration_encoded = []
64
+ duration_uploaded = []
65
+ guids = []
66
+ labels = []
67
+ reports = pad_reports(reports, options) if options["pad"]
68
+ reports = reports.reverse if options[:reverse]
69
+
70
+ reports.each do |report|
71
+ label = report.container
72
+ label = '&lt;blank&gt;' if 'null' == label
73
+
74
+ complete_labels << label
75
+ labels << label.previewize((options[:max_label_length] || 15).to_i)
76
+ assets_uploaded << report.assets_uploaded
77
+ duration_uploaded << report.duration_uploaded_min
78
+ assets_encoded << report.assets_encoded
79
+ duration_encoded << report.duration_encoded_min
80
+ guids << report.id
81
+ end
82
+
83
+ show_screenshots = reports.length <= 10
84
+
85
+ max_labels = 20
86
+ # frequency == 1 if we can display all labels, or a number greater
87
+ # than 1 if we can't
88
+ frequency = (labels.length / max_labels.to_f).ceil
89
+
90
+ xml = options[:builder] ||= Builder::XmlMarkup.new(:indent => 2)
91
+ xml.instruct! :xml, :version=>"1.0", :encoding=>"UTF-8"
92
+ xml.chart do
93
+ xml.series do
94
+ labels.each_with_index do |value, index|
95
+ # only display every nth value where n depends the total
96
+ # number of labels and the max labels we are allowed to
97
+ # display, otherwise clear out the value
98
+ value = "" if index % frequency != 0
99
+ xml.value value, :xid => index
100
+ end
101
+ end
102
+
103
+ xml.graphs do
104
+ #the gid is used in the settings file to set different settings just for this graph
105
+ unless options[:show_uploads] == 'false'
106
+ xml.graph :gid => 'assets_uploaded' do
107
+ assets_uploaded.each_with_index do |value, index|
108
+ xml.value value, :xid => index
109
+ end
110
+ end
111
+ end
112
+
113
+ unless options[:show_duration_uploaded] == 'false'
114
+ xml.graph :gid => 'duration_uploaded' do
115
+ duration_uploaded.each_with_index do |value, index|
116
+ xml.value value, :xid => index
117
+ end
118
+ end
119
+ end
120
+
121
+ unless options[:show_encodes] == 'false'
122
+ xml.graph :gid => 'assets_encoded' do
123
+ assets_encoded.each_with_index do |value, index|
124
+ xml.value value, :xid => index
125
+ end
126
+ end
127
+ end
128
+
129
+ unless options[:show_duration_encoded] == 'false'
130
+ xml.graph :gid => 'duration_encoded' do
131
+ duration_encoded.each_with_index do |value, index|
132
+ xml.value value, :xid => index
133
+ end
134
+ end
135
+ end
136
+ end
137
+ end
138
+ end
139
+ end
@@ -0,0 +1,71 @@
1
+ require 'stats/tzinfo_extensions'
2
+
3
+ class Stats::DiskUsageReport < Stats::EventReport
4
+ set_attributes [ :id, :obj_id, :group, :name, :nickname, :title, :label, :name, :query_time, :start_date, :end_date ]
5
+ set_numeric_attributes [ :actual_bytes, :actual_average_bytes, :bytes, :average_bytes, :objects, :average_objects ]
6
+
7
+ def initialize(options = {})
8
+ @label = "stats" # video unless date
9
+ @obj_id = options[:obj_id ] unless options[:obj_id ].blank?
10
+ @nickname = options[:nickname ] unless options[:nickname ].blank?
11
+ @name = options[:name ] unless options[:name ].blank?
12
+ @group = options[:group ] unless options[:group ].blank?
13
+ @start_date = options[:start_date] unless options[:start_date].blank?
14
+ @end_date = options[:end_date ] unless options[:end_date ].blank?
15
+ @actual_bytes = options[:storage_bytes].to_i
16
+ @actual_average_bytes = options[:average_storage_bytes].to_i
17
+ if options[:storage_bytes]
18
+ @bytes = sprintf("%0.3f", options[:storage_bytes] / 1.gigabyte)
19
+ end
20
+ if options[:average_storage_bytes]
21
+ @average_bytes = sprintf("%0.3f", options[:average_storage_bytes] / 1.gigabyte)
22
+ end
23
+ @objects = options[:objects].to_i
24
+ @average_objects = options[:average_objects].to_i
25
+ @id = @nickname
26
+ end
27
+
28
+ def self.csv_keys
29
+ keys = @nickname.nil? ? [ :id ] : []
30
+ keys += [ :start_date, :end_date, :actual_bytes, :actual_average_bytes, :objects, :average_objects ]
31
+ end
32
+
33
+ def to_csv(options = {})
34
+ the_csv_keys = strip_keys_by_label(self.class.csv_keys, @label)
35
+ the_csv_keys.map do |k|
36
+ self.class.format_csv_value(send(k))
37
+ end.join(',')
38
+ end
39
+
40
+ def to_xml options = {}
41
+ xml = options[:builder] ||= Builder::XmlMarkup.new(:indent => options[:indent])
42
+ xml.instruct! unless options[:skip_instruct]
43
+
44
+ xml.__send__(@label) do
45
+ xml.id @nickname unless @nickname.nil?
46
+ xml.start_date @start_date.strftime("%Y/%m/%d") unless @start_date.nil?
47
+ xml.end_date @end_date.strftime("%Y/%m/%d") unless @end_date.nil?
48
+ xml.bytes @actual_bytes
49
+ xml.average_bytes @actual_average_bytes if @group =~ /daily|weekly|monthly/
50
+ xml.objects @objects
51
+ xml.average_objects @average_objects if @group =~ /daily|weekly|monthly/
52
+ end
53
+ end
54
+
55
+ def to_json(options = {})
56
+ report = HashWithIndifferentAccess.new
57
+ report['id'] = @nickname unless @nickname.nil?
58
+ report['start_date'] = @start_date.strftime("%Y/%m/%d") unless @start_date.nil?
59
+ report['end_date'] = @end_date.strftime("%Y/%m/%d") unless @end_date.nil?
60
+ report['bytes'] = @actual_bytes
61
+ report['average_bytes'] = @actual_average_bytes if @group =~ /daily|weekly|monthly/
62
+ report['objects'] = @objects
63
+ report['average_objects'] = @average_objects if @group =~ /daily|weekly|monthly/
64
+
65
+ report.to_json
66
+ end
67
+
68
+ def self.to_amcharts(reports, show_date, show_date_range, options = {})
69
+ UsageSample.to_amcharts(reports, options)
70
+ end
71
+ end
@@ -0,0 +1,351 @@
1
+ require 'stats/tzinfo_extensions'
2
+
3
+ class Stats::EventReport < Stats::Report
4
+ extend ActiveSupport::Inflector
5
+ extend ActionView::Helpers::TextHelper
6
+ extend ActionView::Helpers::NumberHelper
7
+
8
+ class Pluralizer
9
+ include Singleton
10
+ extend ActionView::Helpers::TextHelper
11
+ end
12
+
13
+ SUPPRESSED_LABELS = {
14
+ :id => %w[domain metropolitan-area],
15
+ :title => %w[country domain metropolitan-area tag browser device profile],
16
+ :hits => %w[],
17
+ :plays => %w[profile],
18
+ :downloads => %w[domain profile],
19
+ :bytes_transferred => %w[profile],
20
+ }
21
+
22
+ set_attributes [ :id, :date, :start_date, :end_date, :title, :label, :name, :query_time, :stats_type ]
23
+ set_numeric_attributes [ :hits, :plays, :downloads, :bytes_transferred]
24
+
25
+ def set_date(time, scope = nil)
26
+ t = Time.parse time.to_s
27
+ case scope
28
+ when nil
29
+ @date = t
30
+ when :daily
31
+ @date = t.beginning_of_day
32
+ when :monthly
33
+ @start_date = t.beginning_of_month
34
+ @end_date = t.end_of_month
35
+ when :weekly
36
+ @start_date = t.beginning_of_week
37
+ @end_date = @start_date.next_week.yesterday
38
+ end
39
+ end
40
+
41
+ def get_start_date(scope)
42
+ case scope
43
+ when nil, :daily
44
+ @date
45
+ when :monthly, :weekly
46
+ @start_date
47
+ end
48
+ end
49
+
50
+ def to_xml options = {}
51
+ xml = options[:builder] ||= Builder::XmlMarkup.new(:indent => options[:indent])
52
+ xml.instruct! unless options[:skip_instruct]
53
+
54
+ xml.__send__(@label) do
55
+ xml.id @id if @id and not SUPPRESSED_LABELS[:id].include?(@label)
56
+ xml.name @name if @name
57
+ xml.title @title unless SUPPRESSED_LABELS[:title].include?(@label)
58
+ xml.hits @hits
59
+ if @stats_type != 'image'
60
+ [ :plays, :downloads ].each do |key|
61
+ xml.tag!(key, self[key]) unless SUPPRESSED_LABELS[key].include?(@label)
62
+ end
63
+ end
64
+ xml.date(pretty_time(@date)) if @date
65
+ xml.start_date(pretty_time(@start_date)) if @start_date
66
+ xml.end_date(pretty_time(@end_date)) if @end_date
67
+ unless SUPPRESSED_LABELS[:bytes_transferred].include?(@label)
68
+ xml.bytes_transferred @bytes_transferred.to_i
69
+ end
70
+ end
71
+ end
72
+
73
+ def to_csv(options = {})
74
+ klass_keys = options[:images] ? Stats::ImageReport.csv_keys : self.class.csv_keys
75
+ the_csv_keys = strip_keys_by_label(klass_keys, @label)
76
+
77
+ the_csv_keys.map do |k|
78
+ self.class.format_csv_value(send(k))
79
+ end.join(',')
80
+ end
81
+
82
+ def to_json(options = {})
83
+ report = HashWithIndifferentAccess.new
84
+ report['id'] = @id if @id and not SUPPRESSED_LABELS[:id].include?(@label)
85
+ report['name'] = @name if @name
86
+ report['title'] = @title unless SUPPRESSED_LABELS[:title].include?(@label)
87
+
88
+ report['hits'] = @hits
89
+ if @stats_type != 'image'
90
+ [ :plays, :downloads ].each do |key|
91
+ report[key.to_s] = self[key.to_s] unless SUPPRESSED_LABELS[key].include?(@label)
92
+ end
93
+ end
94
+
95
+ report['date'] = pretty_time(@date) if @date
96
+ report['start_date'] = pretty_time(@start_date) if @start_date
97
+ report['end_date'] = pretty_time(@end_date) if @end_date
98
+
99
+ unless SUPPRESSED_LABELS[:bytes_transferred].include?(@label)
100
+ report['bytes_transferred'] = @bytes_transferred.to_i
101
+ end
102
+
103
+ report.to_json
104
+ end
105
+
106
+ def self.get_url(r)
107
+ if 'image' == r.stats_type
108
+ prepend = 'image_'
109
+ type = 'albums'
110
+ elsif 'track' == r.stats_type
111
+ prepend = ''
112
+ type = 'tracks'
113
+ else
114
+ prepend = ''
115
+ type = 'videos'
116
+ end
117
+
118
+ url = if r.id.nil?
119
+ nil
120
+ else
121
+ case r.label
122
+ when "company" then "/companies/#{r.id}/#{prepend}statistics"
123
+ when "video" then "/videos/#{r.id}/statistics"
124
+ when "track" then "/tracks/#{r.id}/statistics"
125
+ end
126
+ end
127
+ url ||= (r.title.blank? ? nil : "/#{type}/#{r.id}/statistics")
128
+
129
+ end
130
+
131
+ def self.csv_keys
132
+ [ :id, :name, :title, :hits, :plays, :downloads, :bytes_transferred, :date, :start_date, :end_date ]
133
+ end
134
+
135
+ def self.to_amcharts(reports, show_date, show_date_range, options = {})
136
+ options["fields"] ||= [ :hits, :plays, :downloads ].reject { |field| SUPPRESSED_LABELS[field].include?(options["group"]) }.map(&:to_s)
137
+ complete_labels = []
138
+ labels = []
139
+ hits = []
140
+ plays = []
141
+ downloads = []
142
+ urls = []
143
+ guids = []
144
+ reports = pad_reports(reports, options) if options["pad"]
145
+ reports = reports.reverse if options[:reverse]
146
+
147
+ reports.each do |r|
148
+ if show_date and !r.date.blank?
149
+ label = "#{r.date.month}/#{r.date.day}"
150
+ elsif show_date_range and !r.start_date.blank? and !r.end_date.blank?
151
+ label = if r.start_date.month == r.end_date.month && r.start_date.day == r.start_date.beginning_of_month.day && r.end_date.day == r.end_date.end_of_month.day
152
+ "#{r.start_date.strftime("%b \'%y")}"
153
+ else
154
+ "#{r.start_date.month}/#{r.start_date.day}-#{r.end_date.month}/#{r.end_date.day}"
155
+ end
156
+ elsif !r.title.blank?
157
+ label = r.title
158
+ elsif !r.name.blank?
159
+ label = r.name
160
+ else
161
+ label = r.id.to_s
162
+ end
163
+
164
+ complete_labels << label
165
+ labels << label.previewize((options[:max_label_length] || 15).to_i)
166
+ hits << r.hits
167
+ if r.stats_type != 'image'
168
+ plays << r.plays
169
+ downloads << r.downloads
170
+ end
171
+
172
+ guids << r.id
173
+ if !options.has_key?(:video_id) && !options.has_key?(:track_id)
174
+ urls << get_url(r)
175
+ else
176
+ urls << nil
177
+ end
178
+ end
179
+
180
+ show_screenshots = false # && reports.length <= 10
181
+
182
+ max_labels = 40
183
+ # frequency == 1 if we can display all labels, or a number greater
184
+ # than 1 if we can't
185
+ if show_date || show_date_range
186
+ frequency = 1
187
+ else
188
+ frequency = (labels.length / max_labels.to_f).ceil
189
+ end
190
+
191
+ xml = options[:builder] ||= Builder::XmlMarkup.new(:indent => 2)
192
+ xml.instruct! :xml, :version=>"1.0", :encoding=>"UTF-8"
193
+ xml.chart do
194
+ xml.series do
195
+ labels.each_with_index do |value, index|
196
+ # only display every nth value where n depends the total
197
+ # number of labels and the max labels we are allowed to
198
+ # display, otherwise clear out the value
199
+ value = "" if index % frequency != 0
200
+ xml.value value, :xid => index
201
+ end
202
+ end
203
+
204
+ xml.graphs do
205
+ #the gid is used in the settings file to set different settings just for this graph
206
+ level = "Hits"
207
+ if options[:fields].include?("hits")
208
+ xml.graph :gid => 'hits' do
209
+ hits.each_with_index do |value, index|
210
+ if value != 0 || complete_labels[index].blank? || show_date || show_date_range
211
+ opts = { :xid => index, :description => complete_labels[index] }
212
+ opts[:url] = urls[index] unless urls[index].blank?
213
+ xml.value value, opts
214
+ end
215
+ end
216
+ end
217
+ end
218
+
219
+ if options[:fields].include?("plays")
220
+ xml.graph :gid => 'plays' do
221
+ plays.each_with_index do |value, index|
222
+ if value != 0 || complete_labels[index].blank? || show_date || show_date_range
223
+ opts = { :xid => index, :description => complete_labels[index] }
224
+ opts[:url] = urls[index] unless urls[index].blank?
225
+ opts[:bullet] = "../videos/#{guids[index]}/screenshots/32w.jpg" if !guids[index].blank? && show_screenshots
226
+ xml.value value, opts
227
+ end
228
+ end
229
+ end
230
+ end
231
+
232
+ if options[:fields].include?("downloads")
233
+ xml.graph :gid => 'downloads' do
234
+ downloads.each_with_index do |value, index|
235
+ if value != 0 || complete_labels[index].blank? || show_date || show_date_range
236
+ opts = { :xid => index, :description => complete_labels[index] }
237
+ opts[:url] = urls[index] unless urls[index].blank?
238
+ xml.value value, opts
239
+ end
240
+ end
241
+ end
242
+ end
243
+ end
244
+ end
245
+ end
246
+
247
+ AMMAP_COUNTRIES = [ "AF", "AX", "AL", "DZ", "AD", "AO", "AI", "AG",
248
+ "AR", "AM", "AW", "AU", "AT", "AZ", "BS", "BH", "BD", "BB", "BY",
249
+ "BE", "BZ", "BJ", "BM", "BT", "BO", "BA", "BW", "BR", "BN", "BG",
250
+ "BF", "BI", "KH", "CM", "CA", "CV", "CF", "TD", "CL", "CN", "CO",
251
+ "KM", "CG", "CD", "CR", "CI", "HR", "CU", "CY", "CZ", "DK", "DJ",
252
+ "DM", "DO", "EC", "EG", "SV", "GQ", "ER", "EE", "ET", "FO", "FK",
253
+ "FJ", "FI", "FR", "GF", "GA", "GM", "GE", "DE", "GH", "GR", "GL",
254
+ "GD", "GP", "GT", "GN", "GW", "GY", "HT", "HN", "HK", "HU", "IS",
255
+ "IN", "ID", "IR", "IQ", "IE", "IL", "IT", "JM", "JP", "JO", "KZ",
256
+ "KE", "KP", "KR", "KW", "KG", "LA", "LV", "LB", "LS", "LR", "LY",
257
+ "LI", "LT", "LU", "MK", "MG", "MW", "MY", "ML", "MT", "MQ", "MR",
258
+ "MU", "MX", "MD", "MN", "ME", "MS", "MA", "MZ", "MM", "NA", "NP",
259
+ "NL", "NC", "NZ", "NI", "NE", "NG", "NO", "OM", "PK", "PW", "PS",
260
+ "PA", "PG", "PY", "PE", "PH", "PL", "PT", "PR", "QA", "RE", "RO",
261
+ "RU", "RW", "KN", "LC", "WS", "ST", "SA", "SN", "RS", "SL", "SG",
262
+ "SK", "SI", "SB", "SO", "ZA", "GS", "ES", "LK", "SD", "SR", "SJ",
263
+ "SZ", "SE", "CH", "SY", "TW", "TJ", "TZ", "TH", "TL", "TG", "TO",
264
+ "TT", "TN", "TR", "TM", "TC", "UG", "UA", "AE", "GB", "US", "UY",
265
+ "UZ", "VU", "VE", "VN", "VG", "EH", "YE", "ZM", "ZW", ]
266
+
267
+ def self.to_ammap_country_data(reports, metric)
268
+ xml = Builder::XmlMarkup.new(:indent => 2)
269
+ zoom, zoom_x, zoom_y = zoom_levels
270
+ xml.map :map_file => 'maps/world.swf', :zoom_x => zoom_x, :zoom_y => zoom_y, :zoom => zoom do
271
+ xml.areas do
272
+ AMMAP_COUNTRIES.each do |country_code|
273
+ report = reports.find { |report| report.id == country_code }
274
+ value = report && report.send(metric) || 0
275
+ title_value = make_title_value(metric, value)
276
+
277
+ attributes = {
278
+ :title => "#{TZInfo::Country.code_to_name(country_code)}: #{title_value}",
279
+ :mc_name => country_code,
280
+ :value => value == 0 ? 0 : sprintf('%.8f', Math.log(value + 1))
281
+ }
282
+ xml.area attributes
283
+ end
284
+ xml.area :mc_name => "borders", :color => "#AAAAAA"
285
+ end
286
+ end
287
+ xml.target!
288
+ end
289
+
290
+ def self.zoom_levels
291
+ return "115%", "-0%", "-20%"
292
+ end
293
+
294
+ def self.to_ammap_metro_data(reports, metric)
295
+ xml = Builder::XmlMarkup.new(:indent => 2)
296
+ zoom, zoom_x, zoom_y = zoom_levels
297
+ color = "Graphs::#{metric.to_s.capitalize}".constantize.new.color1 rescue '85E006'
298
+ xml.map :map_file => 'maps/world.swf', :tl_long => "-168.49", :tl_lat => "83.63", :br_long => "190.3", :br_lat => "-55.58", :zoom_x => zoom_x, :zoom_y => zoom_y, :zoom => zoom do
299
+ xml.movies do
300
+ reports.select { |report| not report.id.blank? }.each do |report|
301
+ latitude, longitude = report.id.split(';')
302
+ bubble_size = calculate_bubble_size(report.send(metric))
303
+ title_value = make_title_value metric, report.send(metric)
304
+ xml.movie(:title => "#{report.name}: #{title_value}",
305
+ :lat => latitude,
306
+ :long => longitude,
307
+ :file => 'target',
308
+ :width => bubble_size,
309
+ :height => bubble_size,
310
+ :color => color,
311
+ :alpha => 65,
312
+ :fixed_size => true)
313
+ end
314
+ end
315
+
316
+ xml.areas do
317
+ AMMAP_COUNTRIES.each do |country_code|
318
+ xml.area :mc_name => country_code, :value => 0
319
+ end
320
+ xml.area :mc_name => "borders", :color => "#AAAAAA"
321
+ end
322
+ end
323
+
324
+ xml.target!
325
+ end
326
+
327
+ MinimumMapBubbleSize = 7
328
+
329
+ def self.calculate_bubble_size value
330
+ return 0 if value == 0
331
+ (MinimumMapBubbleSize + 5.0 * Math.log10(value)).to_i
332
+ end
333
+
334
+ def self.make_title_value metric, value
335
+ if [:bytes_transferred].include? metric
336
+ title_value = number_with_delimiter(number_to_human_size(value)) + ' transferred'
337
+ else
338
+ title_value = Pluralizer.pluralize(number_with_delimiter(value), singularize(metric))
339
+ end
340
+ title_value
341
+ end
342
+
343
+ private
344
+
345
+ def strip_keys_by_label(the_keys, label)
346
+ SUPPRESSED_LABELS.each do |key, labels|
347
+ the_keys = (the_keys - [ key ]) if labels.include?(label)
348
+ end
349
+ the_keys
350
+ end
351
+ end
@@ -0,0 +1,5 @@
1
+ class Stats::ImageReport < Stats::EventReport
2
+ def self.csv_keys
3
+ [ :id, :name, :title, :hits, :bytes_transferred, :date, :start_date, :end_date ]
4
+ end
5
+ end
@@ -0,0 +1,10 @@
1
+ require 'iconv'
2
+
3
+ class String
4
+ def latin_1_to_utf
5
+ return '' if blank?
6
+ Iconv.new('UTF-8', 'ISO-8859-1').iconv self
7
+ end
8
+ end
9
+
10
+
@@ -0,0 +1,48 @@
1
+ class Stats::PercentWatchedReport < Stats::Report
2
+ set_attributes [ :label, :start_date, :end_date, :query_time ]
3
+ set_numeric_attributes [ :percent_watched, :percent_of_watches, :fall_off, :watch_count ]
4
+
5
+ def to_xml options = {}
6
+ xml = options[:builder] ||= Builder::XmlMarkup.new(:indent => options[:indent])
7
+ xml.instruct! unless options[:skip_instruct]
8
+
9
+ xml.__send__(@label) do
10
+ xml.percent_watched @percent_watched
11
+ xml.percent_of_watches @percent_of_watches
12
+ xml.fall_off @fall_off
13
+ end
14
+ end
15
+
16
+ def to_json(options = {})
17
+ report = HashWithIndifferentAccess.new
18
+ report['percent_watched'] = percent_watched
19
+ report['percent_of_watches'] = percent_of_watches
20
+ report['fall_off'] = fall_off
21
+ report.to_json
22
+ end
23
+
24
+ def self.csv_keys
25
+ [ :percent_watched, :percent_of_watches, :fall_off ]
26
+ end
27
+
28
+ def self.to_amcharts(reports, show_date, show_date_range, options = {})
29
+ xml = Builder::XmlMarkup.new
30
+ xml.instruct! :xml, :version => "1.0", :encoding => "UTF-8"
31
+ xml.chart do
32
+ xml.series do
33
+ reports.each_with_index do |report, index|
34
+ xml.value "#{report.percent_watched.to_i}%", :xid => report.percent_watched
35
+ end
36
+ end
37
+
38
+ xml.graphs do
39
+ xml.graph :gid => 'percent_watched' do
40
+ reports.each_with_index do |report, index|
41
+ xml.value sprintf('%.1f', report.percent_of_watches), :xid => report.percent_watched
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+
48
+ end
@@ -0,0 +1,168 @@
1
+ require 'stats/tzinfo_extensions'
2
+
3
+ class Stats::PublishReport < Stats::EventReport
4
+ set_attributes [ :id, :date, :start_date, :end_date, :title, :label, :name, :query_time, :bytes_uploaded, :bytes_encoded, :bytes_uploaded_gb, :bytes_encoded_gb ]
5
+ set_numeric_attributes [ :assets_uploaded, :assets_encoded, :duration_uploaded, :duration_encoded, :duration_uploaded_min, :duration_encoded_min ]
6
+
7
+ def initialize(options = {})
8
+ @start_date = options[:date]
9
+
10
+ if 'day' == options[:group]
11
+ @date = options[:date]
12
+ else
13
+ @end_date = options[:date].advance(options[:advance_by] => 1).advance(:days => -1)
14
+ end
15
+
16
+ @label = options[:label]
17
+
18
+ @assets_uploaded = options[:assets_uploaded]
19
+ @assets_encoded = options[:assets_encoded]
20
+
21
+ @query_time = nil
22
+
23
+ @bytes_uploaded = options[:bytes_uploaded]
24
+ @bytes_uploaded_gb = ('%.2f' % (options[:bytes_uploaded] / 1.gigabyte.to_f)).to_f
25
+
26
+ @bytes_encoded = options[:bytes_encoded]
27
+ @bytes_encoded_gb = ('%.2f' % (options[:bytes_encoded] / 1.gigabyte.to_f)).to_f
28
+
29
+ @duration_uploaded = options[:duration_uploaded]
30
+ @duration_uploaded_min = ('%.2f' % (options[:duration_uploaded] / 60.seconds.to_f)).to_f
31
+
32
+ @duration_encoded = options[:duration_encoded]
33
+ @duration_encoded_min = ('%.2f' % (options[:duration_encoded] / 60.seconds.to_f)).to_f
34
+ end
35
+
36
+ def self.csv_keys
37
+ [ :assets_encoded, :assets_uploaded, :bytes_encoded, :bytes_uploaded, :duration_uploaded, :duration_encoded, :date, :start_date, :end_date ]
38
+ end
39
+
40
+ def to_xml options = {}
41
+ xml = options[:builder] ||= Builder::XmlMarkup.new(:indent => options[:indent])
42
+ xml.instruct! unless options[:skip_instruct]
43
+
44
+ xml.__send__(@label) do
45
+ xml.assets_encoded @assets_encoded
46
+ xml.assets_uploaded @assets_uploaded
47
+ xml.bytes_encoded @bytes_encoded
48
+ xml.bytes_uploaded @bytes_uploaded
49
+ xml.date(pretty_time(@date)) if @date
50
+ xml.duration_encoded @duration_encoded
51
+ xml.duration_uploaded @duration_uploaded
52
+ xml.end_date(pretty_time(@end_date)) if @end_date
53
+ xml.start_date(pretty_time(@start_date)) if @start_date
54
+ end
55
+ end
56
+
57
+ def to_json(options = {})
58
+ report = HashWithIndifferentAccess.new
59
+ report['assets_encoded'] = @assets_encoded
60
+ report['assets_uploaded'] = @assets_uploaded
61
+ report['bytes_encoded'] = @bytes_encoded
62
+ report['bytes_uploaded'] = @bytes_uploaded
63
+ report['date'] = pretty_time(@date) if @date
64
+ report['duration_encoded'] = @duration_encoded
65
+ report['duration_uploaded'] = @duration_uploaded
66
+ report['end_date'] = pretty_time(@end_date) if @end_date
67
+ report['start_date'] = pretty_time(@start_date) if @start_date
68
+
69
+ report.to_json
70
+ end
71
+
72
+ def self.to_amcharts(reports, show_date, show_date_range, options = {})
73
+ assets_encoded = []
74
+ assets_uploaded = []
75
+ bytes_encoded = []
76
+ bytes_uploaded = []
77
+ complete_labels = []
78
+ duration_encoded = []
79
+ duration_uploaded = []
80
+ guids = []
81
+ labels = []
82
+ urls = []
83
+ reports = reports.reverse if options[:reverse]
84
+
85
+ reports.each do |report|
86
+ label = case options[:group]
87
+ when 'daily' ; "#{report.date.month}/#{report.date.day}"
88
+ when 'weekly' ; "#{report.start_date.month}/#{report.start_date.day}-#{report.end_date.month}/#{report.end_date.day}"
89
+ when 'monthly' ; "#{report.start_date.strftime("%b \'%y")}"
90
+ else raise "invalid group: #{options[:group]}"
91
+ end
92
+
93
+ complete_labels << label
94
+ labels << label.previewize((options[:max_label_length] || 15).to_i)
95
+ assets_uploaded << report.assets_uploaded
96
+ duration_uploaded << report.duration_uploaded_min
97
+ bytes_uploaded << report.bytes_uploaded_gb
98
+ assets_encoded << report.assets_encoded
99
+ duration_encoded << report.duration_encoded_min
100
+ bytes_encoded << report.bytes_encoded_gb
101
+ guids << report.id
102
+ end
103
+
104
+ show_screenshots = reports.length <= 10
105
+
106
+ max_labels = 40
107
+ # frequency == 1 if we can display all labels, or a number greater
108
+ # than 1 if we can't
109
+ frequency = (show_date || show_date_range) ? 1 : (labels.length / max_labels.to_f).ceil
110
+
111
+ xml = options[:builder] ||= Builder::XmlMarkup.new(:indent => 2)
112
+ xml.instruct! :xml, :version=>"1.0", :encoding=>"UTF-8"
113
+ xml.chart do
114
+ xml.series do
115
+ labels.each_with_index do |value, index|
116
+ # only display every nth value where n depends the total
117
+ # number of labels and the max labels we are allowed to
118
+ # display, otherwise clear out the value
119
+ value = "" if index % frequency != 0
120
+ xml.value value, :xid => index
121
+ end
122
+ end
123
+
124
+ xml.graphs do
125
+ #the gid is used in the settings file to set different settings just for this graph
126
+ unless options[:show_uploads] == 'false'
127
+ xml.graph :gid => 'assets_uploaded' do
128
+ assets_uploaded.each_with_index do |value, index|
129
+ if value != 0 || show_date || show_date_range
130
+ xml.value value, :xid => index
131
+ end
132
+ end
133
+ end
134
+ end
135
+
136
+ unless options[:show_duration_uploaded] == 'false'
137
+ xml.graph :gid => 'duration_uploaded' do
138
+ duration_uploaded.each_with_index do |value, index|
139
+ if value != 0 || show_date || show_date_range
140
+ xml.value value, :xid => index
141
+ end
142
+ end
143
+ end
144
+ end
145
+
146
+ unless options[:show_encodes] == 'false'
147
+ xml.graph :gid => 'assets_encoded' do
148
+ assets_encoded.each_with_index do |value, index|
149
+ if value != 0 || show_date || show_date_range
150
+ xml.value value, :xid => index
151
+ end
152
+ end
153
+ end
154
+ end
155
+
156
+ unless options[:show_duration_encoded] == 'false'
157
+ xml.graph :gid => 'duration_encoded' do
158
+ duration_encoded.each_with_index do |value, index|
159
+ if value != 0 || show_date || show_date_range
160
+ xml.value value, :xid => index
161
+ end
162
+ end
163
+ end
164
+ end
165
+ end
166
+ end
167
+ end
168
+ end
@@ -0,0 +1,122 @@
1
+ class Stats::Report
2
+ def initialize options = {}
3
+ options.assert_valid_keys self.class.attributes
4
+
5
+ self.class.attributes.each { |a| instance_variable_set "@#{a}", nil }
6
+ options.each { |k, v| instance_variable_set "@#{k}", v }
7
+ self.class.numeric_attributes.each do |a|
8
+ attribute_name = "@#{a}"
9
+ instance_variable_set(attribute_name, 0) if instance_variable_get(attribute_name).blank?
10
+ end
11
+ end
12
+
13
+ def == other
14
+ self.class.attributes.all? do |e|
15
+ attribute_name = "@#{e}"
16
+ instance_variable_get(attribute_name) == other.instance_variable_get(attribute_name)
17
+ end
18
+ end
19
+
20
+ def [] method_name
21
+ send method_name
22
+ end
23
+
24
+ def self.array_to_xml(report_array, options = {})
25
+ report_array.to_xml options.merge({:root => 'statistics-reports'})
26
+ end
27
+
28
+ def self.array_to_csv(report_array, options = {})
29
+ buf = ""
30
+
31
+ unless report_array.first.nil?
32
+ label = report_array.first.label
33
+ new_options = options.merge( { :label => label } )
34
+ the_csv_labels = options[:images] ? Stats::ImageReport.csv_labels : report_array[0].class.csv_labels
35
+ Stats::EventReport::SUPPRESSED_LABELS.each do |key, labels|
36
+ the_csv_labels = strip_label(key, the_csv_labels) if labels.include?(label)
37
+ end
38
+
39
+
40
+ buf << the_csv_labels + "\n" unless report_array.empty?
41
+
42
+ report_array.each do |report|
43
+ buf << report.to_csv(new_options) + "\n"
44
+ end
45
+ end
46
+
47
+ buf
48
+ end
49
+
50
+ def self.strip_label(label_sym, labels_as_str)
51
+ labels = labels_as_str.split(/,/)
52
+ (labels - [ label_sym.to_s ]).join(',')
53
+ end
54
+
55
+ def self.csv_keys
56
+ []
57
+ end
58
+
59
+ def to_csv(options = {})
60
+ klass_keys = options[:images] ? Stats::ImageReport.csv_keys : self.class.csv_keys
61
+ klass_keys.map { |k| self.class.format_csv_value(send(k)) }.join(',')
62
+ end
63
+
64
+ def self.csv_labels
65
+ csv_keys.map { |k| format_csv_value(k.to_s) }.join(',')
66
+ end
67
+
68
+ def self.format_csv_value(value)
69
+ return "\"#{value}\"" if value =~ /,/
70
+ value
71
+ end
72
+
73
+ def self.to_amcharts reports, *options
74
+ return reports.first.class.to_amcharts(reports, *options) unless reports.empty?
75
+ xml = Builder::XmlMarkup.new(:indent => 2)
76
+ xml.instruct! :xml, :version=>"1.0", :encoding=>"UTF-8"
77
+ xml.chart
78
+ end
79
+
80
+ def self.pad_reports(reports, options)
81
+ limit = options["limit"].to_i
82
+ return reports if limit < 1 || reports.length < 1
83
+
84
+ # make a copy of the first report
85
+ clone = reports[0].clone
86
+ # blank out all text fields
87
+ clone.class.send(:attributes).each { |key| clone.send("#{key}=", "") }
88
+ # zero out all numeric fields
89
+ clone.class.send(:numeric_attributes).each { |key| clone.send("#{key}=", 0) }
90
+ # add empty reports at the end
91
+ (limit - reports.length).times { reports << clone }
92
+ reports
93
+ end
94
+
95
+ # class instance variables (@attributes and @numeric_attributes) are used to make
96
+ # validation, initialization, assignment, comparison, and accessor generation generic
97
+ def self.set_attributes attrs
98
+ attr_accessor *attrs
99
+ @attributes ||= []
100
+ @attributes.concat attrs
101
+ end
102
+
103
+ def self.set_numeric_attributes attrs
104
+ @numeric_attributes ||= []
105
+ @numeric_attributes.concat attrs
106
+ set_attributes attrs
107
+ end
108
+
109
+ private
110
+
111
+ def self.numeric_attributes
112
+ @numeric_attributes
113
+ end
114
+
115
+ def self.attributes
116
+ @attributes
117
+ end
118
+
119
+ def pretty_time(t)
120
+ t.strftime "%Y/%m/%d"
121
+ end
122
+ end
@@ -0,0 +1,106 @@
1
+ require 'stats/tzinfo_extensions'
2
+
3
+ class Stats::SourceReport < Stats::EventReport
4
+ set_attributes [ :id, :date, :start_date, :end_date, :title, :label, :name, :query_time, :bytes_uploaded, :bytes_encoded, :bytes_uploaded_gb, :bytes_encoded_gb, :publish_type ]
5
+ set_numeric_attributes [ :publishes, :duration, :duration_min, :webcam_duration_min ]
6
+
7
+ def initialize(options = {})
8
+ @label = options[:label]
9
+ @publish_type = options[:publish_type]
10
+ @publishes = options[:publishes] || 0
11
+ @query_time = nil
12
+ @duration = options[:duration].to_f || 0
13
+ @duration_min = ('%.2f' % (@duration / 60.seconds.to_f)).to_f
14
+ end
15
+
16
+ def self.csv_keys
17
+ [ :publish_type, :publishes, :duration ]
18
+ end
19
+
20
+ def to_csv(options = {})
21
+ the_csv_keys = strip_keys_by_label(self.class.csv_keys, @label)
22
+ the_csv_keys.map do |k|
23
+ self.class.format_csv_value(send(k))
24
+ end.join(',')
25
+ end
26
+
27
+ def to_xml options = {}
28
+ xml = options[:builder] ||= Builder::XmlMarkup.new(:indent => options[:indent])
29
+ xml.instruct! unless options[:skip_instruct]
30
+
31
+ xml.__send__(@label) do
32
+ xml.publish_type @publish_type
33
+ xml.publishes @publishes
34
+ xml.duration @duration
35
+ end
36
+ end
37
+
38
+ def to_json(options = {})
39
+ report = HashWithIndifferentAccess.new
40
+ report['publish_type'] = @publish_type
41
+ report['publishes'] = @publishes
42
+ report['duration'] = @duration
43
+
44
+ report.to_json
45
+ end
46
+
47
+ def self.to_amcharts(reports, show_date, show_date_range, options = {})
48
+ complete_labels = []
49
+ durations = []
50
+ guids = []
51
+ labels = []
52
+ publishes = []
53
+ reports = pad_reports(reports, options) if options["pad"]
54
+ reports = reports.reverse if options[:reverse]
55
+
56
+ reports.each do |report|
57
+ label = report.publish_type
58
+ label = '&lt;blank&gt;' if 'blank' == label
59
+
60
+ complete_labels << label
61
+ labels << label.previewize((options[:max_label_length] || 15).to_i)
62
+ durations << report.duration_min
63
+ publishes << report.publishes
64
+ guids << report.id
65
+ end
66
+
67
+ show_screenshots = reports.length <= 10
68
+
69
+ max_labels = 20
70
+ # frequency == 1 if we can display all labels, or a number greater than 1 if we can't
71
+ frequency = (labels.length / max_labels.to_f).ceil
72
+
73
+ xml = options[:builder] ||= Builder::XmlMarkup.new(:indent => 2)
74
+ xml.instruct! :xml, :version=>"1.0", :encoding=>"UTF-8"
75
+ xml.chart do
76
+ xml.series do
77
+ labels.each_with_index do |value, index|
78
+ # only display every nth value where n depends the total
79
+ # number of labels and the max labels we are allowed to
80
+ # display, otherwise clear out the value
81
+ value = "" if index % frequency != 0
82
+ xml.value value, :xid => index
83
+ end
84
+ end
85
+
86
+ xml.graphs do
87
+ #the gid is used in the settings file to set different settings just for this graph
88
+ unless options[:show_uploads] == 'false'
89
+ xml.graph :gid => 'uploads' do
90
+ publishes.each_with_index do |value, index|
91
+ xml.value value, :xid => index
92
+ end
93
+ end
94
+ end
95
+
96
+ unless options[:show_duration_uploaded] == 'false'
97
+ xml.graph :gid => 'duration_uploaded' do
98
+ durations.each_with_index do |value, index|
99
+ xml.value value, :xid => index
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,13 @@
1
+ require 'tzinfo'
2
+
3
+ class TZInfo::Country
4
+ def self.code_to_name code
5
+ begin
6
+ get(code).name
7
+ rescue TZInfo::InvalidCountryCode
8
+ code
9
+ end
10
+ end
11
+ end
12
+
13
+
@@ -0,0 +1,12 @@
1
+ module Stats; end
2
+
3
+ require 'stats/report'
4
+ require 'stats/event_report'
5
+ require 'stats/breakdown_report'
6
+ require 'stats/disk_usage_report'
7
+ require 'stats/image_report'
8
+ require 'stats/latin_1_to_utf'
9
+ require 'stats/percent_watched_report'
10
+ require 'stats/publish_report'
11
+ require 'stats/source_report'
12
+ require 'stats/tzinfo_extensions'
metadata ADDED
@@ -0,0 +1,74 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: twistage_stats_reports
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 1
7
+ - 0
8
+ - 0
9
+ version: 1.0.0
10
+ platform: ruby
11
+ authors:
12
+ - David Wegman
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2012-11-05 00:00:00 -08:00
18
+ default_executable:
19
+ dependencies: []
20
+
21
+ description: Shared data structures and logic for interacting with the Twistage Stats service
22
+ email: dwegman@twistage.com
23
+ executables: []
24
+
25
+ extensions: []
26
+
27
+ extra_rdoc_files: []
28
+
29
+ files:
30
+ - lib/twistage_stats_reports.rb
31
+ - lib/stats/breakdown_report.rb
32
+ - lib/stats/disk_usage_report.rb
33
+ - lib/stats/event_report.rb
34
+ - lib/stats/image_report.rb
35
+ - lib/stats/latin_1_to_utf.rb
36
+ - lib/stats/percent_watched_report.rb
37
+ - lib/stats/publish_report.rb
38
+ - lib/stats/report.rb
39
+ - lib/stats/source_report.rb
40
+ - lib/stats/tzinfo_extensions.rb
41
+ has_rdoc: true
42
+ homepage: http://rubygems.org/gems/twistage_stats_reports
43
+ licenses: []
44
+
45
+ post_install_message:
46
+ rdoc_options: []
47
+
48
+ require_paths:
49
+ - lib
50
+ required_ruby_version: !ruby/object:Gem::Requirement
51
+ none: false
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ segments:
56
+ - 0
57
+ version: "0"
58
+ required_rubygems_version: !ruby/object:Gem::Requirement
59
+ none: false
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ segments:
64
+ - 0
65
+ version: "0"
66
+ requirements: []
67
+
68
+ rubyforge_project:
69
+ rubygems_version: 1.3.7
70
+ signing_key:
71
+ specification_version: 3
72
+ summary: Twistage Stats Reports
73
+ test_files: []
74
+