Skip to content

Instantly share code, notes, and snippets.

@stepheneb
Created February 17, 2009 10:54
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 stepheneb/65683 to your computer and use it in GitHub Desktop.
Save stepheneb/65683 to your computer and use it in GitHub Desktop.
#!/usr/bin/env ruby
require 'rubygems'
require 'activerecord'
require 'builder'
require 'fileutils'
require 'yaml'
require 'erb'
require 'zlib'
require 'base64'
require 'hpricot'
require 'open-uri'
require 'optparse'
require 'rdoc/usage'
require 'ostruct'
require 'date'
require 'uuidtools'
# @logger = Logger.new $stderr
# ActiveRecord::Base.logger = @logger
# ActiveRecord::Base.colorize_logging = false
# http://www.graphviz.org/pdf/dotguide.pdf
# show_wg_bundles.rb 32937 | dot -Tpng > bundles_wg_32937.png ; open bundles_wg_32937.png
# Creating a new wg_database.yml database configuration file:
#
# wg_db_config = {
# :host => 'host.org',
# :username => 'username',
# :password => 'password',
# :database => 'database'
# }
#
# File.open('wg_database.yml', 'w') {|f| f.write wg_db_config.to_yaml }
#
wg_db_config = YAML::load(IO.read('wg_database.yml'))
pool = ActiveRecord::Base.establish_connection(
:adapter => RUBY_PLATFORM =~ /java/ ? 'jdbcmysql' : 'mysql',
:host => wg_db_config[:host],
:username => wg_db_config[:username],
:password => wg_db_config[:password],
:database => wg_db_config[:database]
)
module B64
class B64
def self.folding_encode(str, eol = "\n", limit = 60)
[str].pack('m')
end
def self.encode(str)
[str].pack('m').tr( "\r\n", '')
end
def self.decode(str, strict = false)
str.unpack('m').first
end
end
end
class SailUser < ActiveRecord::Base
has_many :workgroup_memberships
has_many :workgroups, :through => :workgroup_memberships, :select => 'DISTINCT workgroups.*'
def name
"#{first_name} #{last_name}"
end
end
class Sock < ActiveRecord::Base
belongs_to :bundle
belongs_to :pod
def text(ignore_file = true)
value = case self.pod.encoding
when 'gzip+b64'
self.unpack_gzip_b64_value(ignore_file)
when 'escaped'
self.unescape_value
end
case self.pod.mime_type
when /xml/
value ? value : self.value
when /java_object/
"java object: #{value.length.to_s} bytes"
when /text/
value ? value : self.value
else
"can't determine how to render this sock as text"
end
end
def unescape_value
CGI.unescapeHTML(self.value)
end
def unpack_gzip_b64_value(ignore_file = false)
begin
if self.pod.bytearray?
if ((! ignore_file) && File.exist?(self.path + self.filename_decoded))
File.read(self.path + self.filename_decoded)
else
Zlib::GzipReader.new(StringIO.new(B64::B64.decode(self.value))).read
end
else
""
end
rescue => e
if e == Zlib::GzipFile::Error
$!
else
"Couldn't match the error: #{e}"
end
end
end
# instance method returns filesystem path to
# sock directory root
def path
"#{self.bundle.path}socks/"
end
def filename_raw
"raw/sock_#{self.id.to_s}_#{self.pod.pas_type}_#{self.pod.encoding}"
end
def filename_decoded
"decoded/sock_#{self.id.to_s}_#{self.pod.pas_type}.#{self.pod.extension}"
end
end
class Pod < ActiveRecord::Base
has_many :socks
has_many :bundles, :through => :socks
@@pod_shape_map = {'[B' => 'bytearray', '' => 'text'}
@@pod_type_keys = ['mime_type', 'encoding', 'pas_type', 'extension']
@@pod_type_map = {
['bytearray', 'ot.learner.data' ] => ['application/xml+otrunk', 'gzip+b64', 'ot_learner_data', 'otml'],
['bytearray', 'otrunk_drawing' ] => ['application/xml+otrunk-drawing', 'gzip+b64', 'otrunk_drawing', 'otml'],
['bytearray', 'trialData' ] => ['java_object/gzip+b64', 'gzip+b64', 'trial_data', 'pojo'],
['bytearray', 'findingsData' ] => ['java_object/gzip+b64', 'gzip+b64', 'findings_data', 'pojo'],
['bytearray', '' ] => ['java_object/gzip+b64', 'gzip+b64', 'generic_pas_object', 'pojo'],
['text', 'model.activity.data'] => ['application/xml+pas-modelreport', 'escaped', 'model_activity_data', 'xml' ],
['text', 'modelActivityData' ] => ['application/xml+pas-modelreport', 'escaped', 'model_activity_data', 'xml' ],
['text', 'navigation_log' ] => ['application/xml+pas-navigation-log', 'escaped', 'navigation_log', 'xml' ],
['text', 'curnit_map' ] => ['application/xml+pas-curnit-map', 'escaped', 'curnit_map', 'xml' ],
['text', 'session_state' ] => ['application/xml+pas-session-state', 'escaped', 'session_state', 'xml' ],
['text', 'airbag99' ] => ['application/xml+svg', 'escaped', 'pedraw', 'svg' ],
['text', 'airbag999' ] => ['application/xml+svg', 'escaped', 'pedraw', 'svg' ],
['text', 'undefined' ] => ['text/plain', 'escaped', 'note', 'txt' ]
}
cattr_reader :pod_shape_map
cattr_reader :pod_type_keys
cattr_reader :pod_type_map
# calculates and returns a hash: {:mime_type, :encoding, :pas_type, :extension]
def kind
shape = self.rim_shape
name = self.rim_name
name = name[/(undefined).*/, 1] || name
value_array = @@pod_type_map[[shape, name]]
if value_array == nil
case shape
when 'bytearray'
value_array = @@pod_type_map[[shape, '']]
when 'text'
value_array = @@pod_type_map[[shape, 'undefined']]
end
end
Hash[*[@@pod_type_keys, value_array].transpose.flatten]
end
def bytearray?
self.rim_shape == 'bytearray'
end
# returns filesystem path to
# pod directory root
def path
"#{self.curnit.path}pods/#{self.uuid}/"
end
def filename
self.html_body.blank? ? nil : "#{self.rim_name}_body.html"
end
end
class BundleContent < ActiveRecord::Base
has_one :bundle
def ot_learner_data
sock = self.bundle.socks.detect{|i| i.pod.rim_name == 'ot.learner.data'}
if sock
ot = sock.text
if ot =~ /anon_single_user/
ot [/anon_single_user/] = self.bundle.workgroup.member_names
end
ot
else
self.bundle.workgroup.blank_ot_learner_data
end
end
end
class Bundle < ActiveRecord::Base
belongs_to :workgroup
belongs_to :bundle_content
has_many :socks
attr_accessor :parent
def calc_modified_time
# take the sail_session_start_time and increment by the msOffset time of the newest sockentry in the bundle
starttime = self.sail_session_start_time
if ! starttime
return (self.sail_session_end_time ? self.sail_session_end_time : self.created_at)
else
if self.socks.count > 0
modtime = starttime + (self.socks.sort{|a,b| b.ms_offset <=> a.ms_offset}.compact[0].ms_offset/1000)
return modtime.getgm
else
return (self.sail_session_end_time ? self.sail_session_end_time : self.created_at)
end
end
end
end
class WorkgroupMembership < ActiveRecord::Base
belongs_to :workgroup
belongs_to :sail_user
end
class Workgroup < ActiveRecord::Base
belongs_to :offering
has_many :bundles
has_many :workgroup_memberships
has_many :sail_users, :through => :workgroup_memberships do
def version(version)
find :all, :conditions => ['version = ?', version]
end
end
def members
self.sail_users.version(self.version)
end
def member_names
self.members.collect {|m| m.name}.join(', ')
end
def blank_ot_learner_data
xml = Builder::XmlMarkup.new(:indent=>2)
xml.otrunk("id" => UUID.timestamp_create().to_s) {
xml.imports {
xml.import("class" => "org.concord.otrunk.OTStateRoot")
xml.import("class" => "org.concord.otrunk.user.OTUserObject")
xml.import("class" => "org.concord.otrunk.user.OTReferenceMap")
}
xml.objects {
xml.OTStateRoot("formatVersionString" => "1.0") {
xml.userMap {
userkey = self.uuid
xml.entry("key" => userkey) {
xml.OTReferenceMap {
xml.user {
xml.OTUserObject("name" => "#{self.member_names}", "id" => userkey)
}
}
}
}
}
}
}
end
end
class Offering < ActiveRecord::Base
has_many :workgroups
has_many :bundles, :through => :workgroups
end
def find_parent(bundle, list)
# sort the list by sail_session_modified_time
sorted_list = list.sort {|a,b| a.sail_session_modified_time <=> b.sail_session_modified_time}
# find all bundles with a modified time earlier than the current bundle's start time
# this means the bundle should have existed in the SDS when the current bundle's session was launched
prior_bundles = sorted_list.select{|c| c.sail_session_modified_time < bundle.sail_session_start_time}
# return the most recent one
return prior_bundles[-1]
end
class App
VERSION = '0.0.1'
attr_reader :options
def initialize(arguments, stdin)
@arguments = arguments
@stdin = stdin
# Set defaults
@options = OpenStruct.new
@options.verbose = false
@options.quiet = false
# TO DO - add additional defaults
end
# Parse options, check arguments, then process the command
def run
if parsed_options? && arguments_valid?
output_options if @options.verbose # [Optional]
process_arguments
process_command
else
output_usage
end
end
protected
def parsed_options?
# Specify options
opts = OptionParser.new
opts.on('-v', '--version') { output_version ; exit 0 }
opts.on('-h', '--help') { output_help }
# opts.on('-V', '--verbose') { @options.verbose = true }
# opts.on('-q', '--quiet') { @options.quiet = true }
# TO DO - add additional options
opts.parse!(@arguments) rescue return false
process_options
true
end
# Performs post-parse processing on options
def process_options
# @options.verbose = false if @options.quiet
end
def output_options
puts "Options:\n"
@options.marshal_dump.each do |name, val|
puts " #{name} = #{val}"
end
end
# True if required arguments were provided
def arguments_valid?
# TO DO - implement your real logic here
true if @arguments.length == 1
end
# Setup the arguments
def process_arguments
@workgroup = Workgroup.find(@arguments[0]);
@names = @workgroup.member_names
# @names = @names.join(', ') if @names.length > 1
# TO DO - place in local vars, etc
end
def output_help
output_version
RDoc::usage() #exits app
end
def output_usage
RDoc::usage('usage') # gets usage from comments above
end
def output_version
puts "#{File.basename(__FILE__)} version #{VERSION}"
end
def process_command
nbundles = []
# sort the bundles by sail_session_start_time
sorted = @workgroup.bundles.sort{|a,b| a.sail_session_start_time <=> b.sail_session_start_time}
# find each bundle's parent and then save the bundle into the nbundles list
sorted.each do |b|
b.sail_session_modified_time = b.calc_modified_time
b.parent = find_parent(b, nbundles)
nbundles << b
end
dotfile = ''
# print the digraph in DOT format
dotfile << "digraph graphname\n{\n"
wg_dir = "wg_#{@workgroup.id}"
FileUtils.mkdir(wg_dir) unless File.exists?(wg_dir)
Dir.chdir(wg_dir) do
nbundles.each do |b|
FileUtils.rm_f("#{b.id}") if File.exists?("#{b.id}")
File.open("#{b.id}.otml", 'w') {|f| f.write b.bundle_content.ot_learner_data }
if b.parent
dotfile << "\"#{b.parent.id} (#{b.parent.bundle_content.content.size})\" -> \"#{b.id} (#{b.bundle_content.content.size})\";\n"
end
end
end
dotfile << "labelloc=t;\n"
dotfile << "label=\"Offering: #{@workgroup.offering.name}; Workgroup: #{@workgroup.id}; #{@names}\";\n"
dotfile << "}"
puts dotfile
end
def process_standard_input
input = @stdin.read
# TO DO - process input
# [Optional]
#@stdin.each do |line|
# # TO DO - process each line
#end
end
end
# Create and run the application
app = App.new(ARGV, STDIN)
app.run
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment