twistage_stats_reports 1.0.0

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