Skip to content

Instantly share code, notes, and snippets.

@ttscoff
Last active January 2, 2022 10:40
Show Gist options
  • Save ttscoff/7431094 to your computer and use it in GitHub Desktop.
Save ttscoff/7431094 to your computer and use it in GitHub Desktop.
Command line bar chart from JSON data (for GeekTool, et al)
#!/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