|
|
@@ -0,0 +1,313 @@ |
|
|
#!/usr/bin/env ruby |
|
|
# encoding: utf-8 |
|
|
|
|
|
# Outputs a vertical bar chart from date-based JSON data |
|
|
# Requires the JSON rubygem: `[sudo] gem install json` |
|
|
require 'date' |
|
|
require 'open-uri' |
|
|
require 'rubygems' |
|
|
require 'json' |
|
|
|
|
|
# Data source URL (sample output at the end of script) |
|
|
data_url = "http://api.feedpress.it/feeds/..." |
|
|
# Parent container key for data object |
|
|
main_container = 'stats' |
|
|
# Repeated key for date/stats objects |
|
|
date_container = 'day' |
|
|
# keys for amounts to collect |
|
|
total_containers = ['greader','other','direct'] |
|
|
|
|
|
# How many columns to output |
|
|
columns = 30 |
|
|
# Total height will be determined by min and max valudes |
|
|
# scaled to max_rows |
|
|
max_rows = 15 |
|
|
|
|
|
# If "test" is passed as an argument, load test data from end of script |
|
|
if ARGV[0] == "test" || !data_url || data_url.strip == "" |
|
|
input = DATA.read |
|
|
columns = 30 |
|
|
max_rows = 18 |
|
|
main_container = 'stats' |
|
|
date_container = 'day' |
|
|
total_containers = ['greader','other','direct'] |
|
|
else |
|
|
# Otherwise, read data from the data url |
|
|
input = open(URI.parse(data_url)).read |
|
|
end |
|
|
|
|
|
json = JSON.parse(input) |
|
|
|
|
|
# Create two arrays, one for dates and one for totals |
|
|
dates = [] |
|
|
totals = [] |
|
|
# Step through data objects to populate both arrays sequentially |
|
|
json[main_container].each { |day| |
|
|
dates.push(Time.at(day[date_container]).to_datetime.strftime('%m/%d')) |
|
|
total = 0 |
|
|
for item in total_containers |
|
|
total += day[item].to_i |
|
|
end |
|
|
|
|
|
totals.push(total) |
|
|
} |
|
|
|
|
|
# Find the highest and lowest values to determine bar heights |
|
|
max = totals.sort[-1] |
|
|
min = totals.sort[0] |
|
|
|
|
|
# Trim data arrays down to the maximum number of columns |
|
|
# defined above |
|
|
dates = dates.reverse[0..columns] |
|
|
totals = totals.reverse[0..columns] |
|
|
|
|
|
# Determine number or rows to generate |
|
|
topline = max_rows |
|
|
div = max / topline |
|
|
bottomline = min/div |
|
|
|
|
|
# Output each row. If the total for the column is greater |
|
|
# than or equal to the scaled row counter, output a chunk |
|
|
# of the bar |
|
|
topline.times do |
|
|
totals.each { |num| |
|
|
if num / div > topline |
|
|
print "◼ " |
|
|
else |
|
|
print " " |
|
|
end |
|
|
} |
|
|
if topline + (max_rows/10).round == bottomline |
|
|
puts |
|
|
break |
|
|
else |
|
|
topline -= 1 |
|
|
puts |
|
|
end |
|
|
end |
|
|
|
|
|
# Calculate average across all totals |
|
|
avg = totals.inject(0.0) { |sum, el| sum + el } / totals.size |
|
|
|
|
|
# Output a legend with today, peak and average |
|
|
puts "#{dates[0]} - #{dates[-1]} ⇒Today: #{totals[-1]} | Peak: #{max} | Average: #{avg.round}" |
|
|
puts |
|
|
|
|
|
# Sample data for testing |
|
|
__END__ |
|
|
|
|
|
{ |
|
|
"stats": [ |
|
|
{ |
|
|
"day": 1382914800, |
|
|
"greader": 10195, |
|
|
"other": 4409, |
|
|
"direct": 879, |
|
|
"newsletter": 0 |
|
|
}, |
|
|
{ |
|
|
"day": 1382824800, |
|
|
"greader": 10174, |
|
|
"other": 4327, |
|
|
"direct": 843, |
|
|
"newsletter": 0 |
|
|
}, |
|
|
{ |
|
|
"day": 1382738400, |
|
|
"greader": 10172, |
|
|
"other": 4173, |
|
|
"direct": 861, |
|
|
"newsletter": 0 |
|
|
}, |
|
|
{ |
|
|
"day": 1382652000, |
|
|
"greader": 10182, |
|
|
"other": 4195, |
|
|
"direct": 853, |
|
|
"newsletter": 0 |
|
|
}, |
|
|
{ |
|
|
"day": 1382565600, |
|
|
"greader": 10163, |
|
|
"other": 4207, |
|
|
"direct": 851, |
|
|
"newsletter": 0 |
|
|
}, |
|
|
{ |
|
|
"day": 1382479200, |
|
|
"greader": 10135, |
|
|
"other": 4206, |
|
|
"direct": 839, |
|
|
"newsletter": 0 |
|
|
}, |
|
|
{ |
|
|
"day": 1382392800, |
|
|
"greader": 8196, |
|
|
"other": 4437, |
|
|
"direct": 850, |
|
|
"newsletter": 0 |
|
|
}, |
|
|
{ |
|
|
"day": 1382306400, |
|
|
"greader": 8181, |
|
|
"other": 4482, |
|
|
"direct": 819, |
|
|
"newsletter": 0 |
|
|
}, |
|
|
{ |
|
|
"day": 1382220000, |
|
|
"greader": 9968, |
|
|
"other": 4413, |
|
|
"direct": 858, |
|
|
"newsletter": 0 |
|
|
}, |
|
|
{ |
|
|
"day": 1382133600, |
|
|
"greader": 9972, |
|
|
"other": 4413, |
|
|
"direct": 859, |
|
|
"newsletter": 0 |
|
|
}, |
|
|
{ |
|
|
"day": 1382047200, |
|
|
"greader": 10050, |
|
|
"other": 4375, |
|
|
"direct": 842, |
|
|
"newsletter": 0 |
|
|
}, |
|
|
{ |
|
|
"day": 1381960800, |
|
|
"greader": 10056, |
|
|
"other": 4310, |
|
|
"direct": 821, |
|
|
"newsletter": 0 |
|
|
}, |
|
|
{ |
|
|
"day": 1381874400, |
|
|
"greader": 10048, |
|
|
"other": 4153, |
|
|
"direct": 773, |
|
|
"newsletter": 0 |
|
|
}, |
|
|
{ |
|
|
"day": 1381788000, |
|
|
"greader": 10052, |
|
|
"other": 4108, |
|
|
"direct": 733, |
|
|
"newsletter": 0 |
|
|
}, |
|
|
{ |
|
|
"day": 1381701600, |
|
|
"greader": 10055, |
|
|
"other": 4228, |
|
|
"direct": 701, |
|
|
"newsletter": 0 |
|
|
}, |
|
|
{ |
|
|
"day": 1381615200, |
|
|
"greader": 10062, |
|
|
"other": 4371, |
|
|
"direct": 650, |
|
|
"newsletter": 0 |
|
|
}, |
|
|
{ |
|
|
"day": 1381528800, |
|
|
"greader": 10079, |
|
|
"other": 4474, |
|
|
"direct": 623, |
|
|
"newsletter": 0 |
|
|
}, |
|
|
{ |
|
|
"day": 1381442400, |
|
|
"greader": 10112, |
|
|
"other": 4590, |
|
|
"direct": 584, |
|
|
"newsletter": 0 |
|
|
}, |
|
|
{ |
|
|
"day": 1381356000, |
|
|
"greader": 10245, |
|
|
"other": 4789, |
|
|
"direct": 571, |
|
|
"newsletter": 0 |
|
|
}, |
|
|
{ |
|
|
"day": 1381269600, |
|
|
"greader": 10150, |
|
|
"other": 5101, |
|
|
"direct": 585, |
|
|
"newsletter": 0 |
|
|
}, |
|
|
{ |
|
|
"day": 1381183200, |
|
|
"greader": 10155, |
|
|
"other": 5255, |
|
|
"direct": 583, |
|
|
"newsletter": 0 |
|
|
}, |
|
|
{ |
|
|
"day": 1381096800, |
|
|
"greader": 10172, |
|
|
"other": 5266, |
|
|
"direct": 554, |
|
|
"newsletter": 0 |
|
|
}, |
|
|
{ |
|
|
"day": 1381010400, |
|
|
"greader": 10201, |
|
|
"other": 5247, |
|
|
"direct": 585, |
|
|
"newsletter": 0 |
|
|
}, |
|
|
{ |
|
|
"day": 1380924000, |
|
|
"greader": 10232, |
|
|
"other": 5285, |
|
|
"direct": 582, |
|
|
"newsletter": 0 |
|
|
}, |
|
|
{ |
|
|
"day": 1380837600, |
|
|
"greader": 10235, |
|
|
"other": 5349, |
|
|
"direct": 570, |
|
|
"newsletter": 0 |
|
|
}, |
|
|
{ |
|
|
"day": 1380751200, |
|
|
"greader": 10267, |
|
|
"other": 5288, |
|
|
"direct": 556, |
|
|
"newsletter": 0 |
|
|
}, |
|
|
{ |
|
|
"day": 1380664800, |
|
|
"greader": 10308, |
|
|
"other": 5042, |
|
|
"direct": 540, |
|
|
"newsletter": 0 |
|
|
}, |
|
|
{ |
|
|
"day": 1380578400, |
|
|
"greader": 10328, |
|
|
"other": 4828, |
|
|
"direct": 551, |
|
|
"newsletter": 0 |
|
|
}, |
|
|
{ |
|
|
"day": 1380492000, |
|
|
"greader": 10364, |
|
|
"other": 4630, |
|
|
"direct": 574, |
|
|
"newsletter": 0 |
|
|
}, |
|
|
{ |
|
|
"day": 1380405600, |
|
|
"greader": 10350, |
|
|
"other": 4479, |
|
|
"direct": 597, |
|
|
"newsletter": 0 |
|
|
} |
|
|
], |
|
|
"code": 1 |
|
|
} |