#!/usr/bin/env ruby require 'rbbt/workflow' require 'rbbt-util' require 'fileutils' require 'rbbt/util/simpleopt' require 'rbbt/workflow/step' require 'rbbt/util/misc' require 'rbbt-util' require 'rbbt/util/simpleopt' $0 = "rbbt #{$previous_commands*""} #{ File.basename(__FILE__) }" if $previous_commands options = SOPT.setup < -h--help Help -p--plot* draw the dependency plot into -i--inputs* List of inputs to print -if--info_fields* List of info fields to print EOF SOPT.usage if options[:help] $inputs = (options[:inputs] || "").split(",") $info_fields = (options[:info_fields] || "").split(",") file = ARGV.shift $seen = [] def get_step(file) file = File.expand_path(file) file = file.sub(/\.(info|files)/,'') $seen << file Workflow.load_step file end def status_msg(status) color = case status.to_sym when :error, :aborted, :missing, :dead :red when :streaming, :started :cyan when :done :green when :noinfo :blue when :dependencies, :waiting, :setyp :yellow else if status.to_s.index ">" :cyan else :cyan end end Log.color(color, status.to_s) end def report_msg(status, name, path, info = nil) parts = path.sub(/\{.*/,'').sub(/#{Regexp.quote(name)}$/,'').split "/" task = Log.color(:yellow, parts.pop) workflow = Log.color(:magenta, parts.pop) if status.to_s == 'noinfo' and parts.last != 'jobs' task, status, workflow = Log.color(:yellow, info[:task_name]), Log.color(:blue, "file"), Log.color(:magenta, "-") end str = if not Open.remote?(path) and (File.exists?(path) and $main_mtime and ($main_mtime - File.mtime(path)) < 0) status_msg(status.to_s) << " " << [workflow, task, path].compact * " " << " (#{Log.color(:red, "Mtime out of sync") })" else status_msg(status.to_s) << " " << [workflow, task, path].compact * " " end if $inputs and $inputs.any? job_inputs = Workflow.load_step(path).recursive_inputs.to_hash IndiferentHash.setup(job_inputs) $inputs.each do |input| value = job_inputs[input] next if value.nil? value_str = Misc.fingerprint(value) str << "\t#{Log.color :magenta, input}=#{value_str}" end end if $info_fields and $info_fields.any? $info_fields.each do |field| IndiferentHash.setup(info) value = info[field] next if value.nil? value_str = Misc.fingerprint(value) str << "\t#{Log.color :magenta, field}=#{value_str}" end end str << "\n" end def report(step, offset = 0, task = nil) info = step.info || {} info[:task_name] = task path = step.path status = info[:status] || :missing status = "remote" if Open.remote?(path) name = info[:name] || File.basename(path) status = :unsync if status == :done and not File.exist? path str = " " * offset str << report_msg(status, name, path, info) info[:dependencies].each do |task,name,path| new = ! $seen.include?(path) dep = get_step path if new str << report(dep, offset + 1, task) else str << Log.color(:blue, Log.uncolor(report(dep, offset+1, task))) end end if info[:dependencies] str end step = get_step file $main_mtime = File.exist?(step.path) ? File.mtime(step.path) : nil def adjacency(step) info = step.info || {} path = step.path status = info[:status] || :missing status = "remote" if Open.remote?(path) if status == 'remote' workflow, task, name = path.sub(/\{.*/,'').split("/")[-3..-1] else workflow, task, name = path.split("/")[-3..-1] end name = name.split(":").last status = :unsync if status == :done and not File.exist? path shapes = Hash.new "circle" edge_info = {:status => status, :workflow => workflow, :task => task, :name => name, :label => task, :shape => shapes[workflow], :color => status == 'remote' ? 'blue' : 'green'} id = Misc.digest(path) edges = [] node_info = {} node_info[id] = edge_info if info[:dependencies] info[:dependencies].each do |task,name,path| dep = get_step path _id, _edges, _node_info = adjacency(dep) edges << [_id, id] edges.concat _edges node_info.merge!(_node_info) end end [id, edges, node_info] end if options[:plot] id, edges, node_info = adjacency(step) node_info[id][:color] = 'red' TmpFile.with_file do |edge_file| Open.write(edge_file) do |f| f.puts "from,to" edges.each do |from,to| f.puts [from,to]*"," end end TmpFile.with_file do |node_info_file| Open.write(node_info_file) do |f| fields = node_info.first.last.keys f.puts "id," + fields * "," node_info.each do |id,info| f.puts ([id] + info.values_at(*fields)) * "," end end require 'rbbt/util/R' R.run <<-EOF nodes <- read.csv("#{node_info_file}", header=T, as.is=T) links <- read.csv("#{edge_file}", header=T, as.is=T) library(igraph) net <- graph.data.frame(links, nodes, directed=T) net <- simplify(net, remove.multiple = F, remove.loops = T) png("#{options[:plot]}", width=1000, height=1000) plot(net, edge.arrow.size=0.4, vertex.label=net$label, vertex.color=net$color) dev.off() EOF end end else puts report(step).strip end