-
-
Save jbeales/5b9de8cf1949c0fdc5a372ddc1f130ef to your computer and use it in GitHub Desktop.
Command line bar chart from JSON data (for GeekTool, et al)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env ruby | |
# encoding: utf-8 | |
# Brett Terpstra 2013, WTF license <http://www.wtfpl.net/txt/copying/> | |
# 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 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment