Skip to content

Instantly share code, notes, and snippets.

@dvogel
Created January 31, 2014 22:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dvogel/8744642 to your computer and use it in GitHub Desktop.
Save dvogel/8744642 to your computer and use it in GitHub Desktop.
A script to generate a Graphviz file for what happens to bills in congress
require 'set'
require 'pp'
require 'json'
require 'date'
require 'bigdecimal'
module BillStateGraphs
class BillAction
attr_accessor :acted_at
def initialize (hash)
@hash = hash
@acted_at = DateTime.parse(hash['acted_at'])
end
def status
@hash['status']
end
end
class Bill
attr_accessor :actions
def initialize (hash)
@hash = hash
@actions = @hash['actions'].map{ |act| BillAction.new(act) }
@actions.sort_by!(&:acted_at)
end
def transitions
transition_list = []
status = nil
while actions.length > 0
act = actions.shift
if not act.status.nil? and act.status != status
transition_list.push [status, act.status]
status = act.status
end
end
return transition_list
end
end
def self.node_id_for_status (status)
# Graphviz doesn't like colons
if status.nil?
"nil"
else
status.gsub(/[^_a-zA-Z0-9]/, '_')
end
end
def self.run (congress_dir, congresses, matrix_path, graph_path)
statuses = Set.new
matrix = {}
total = BigDecimal.new(0)
congresses.map(&:to_i).each do |cong_num|
bill_file_paths = Dir.glob(File.join(congress_dir,
cong_num.to_s,
'bills',
'*',
'*',
'data.json'))
bill_file_paths.each do |path|
File.open(path, 'r') do |bill_file|
hash = JSON.load(bill_file)
bill = Bill.new(hash)
bill.transitions.each do |a, b|
statuses.add(a)
statuses.add(b)
row = matrix.fetch(a, {})
cnt = row.fetch(b, 0) + 1
row[b] = cnt
matrix[a] = row
total += 1
end
end
end
end
statuses = statuses.to_a.map(&:to_s).sort!
maxlen = statuses.map(&:length).max
format = "%#{maxlen}s " * (statuses.length + 1) + "\n"
File.open(matrix_path, 'w') do |matrix_file|
matrix_file.printf(format, "", *statuses)
statuses.each do |a|
row = matrix.fetch(a, {})
values = statuses.map{ |b| row.fetch(b, '') }
matrix_file.printf(format, a, *values)
end
end
File.open(graph_path, 'w') do |graph_file|
graph_file.puts("digraph {")
matrix.each do |a, row|
row.each do |b, cnt|
pct = (BigDecimal.new(cnt) * 100 / total).round(2).to_f.to_s
graph_file.puts("#{node_id_for_status(a)} -> #{node_id_for_status(b)} [label=\"#{cnt}\\n(#{pct}%)\"] ;")
end
end
graph_file.puts("}")
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment