skip_before_filter :check_user_agreements, :only => [:show_file]
def show_pane_list
- %w(files attributes provenance provenance_graph)
+ %w(files attributes provenance provenance_graph used_by)
end
def index
if params[:search].andand.length.andand > 0
Collection.where(uuid: @object.uuid).each do |u|
@prov_svg = ProvenanceHelper::create_provenance_graph u.provenance, "provenance_svg", {:direction => :bottom_up, :combine_jobs => :script_only}
+ @used_by_svg = ProvenanceHelper::create_provenance_graph u.used_by, "used_by_svg", {:direction => :top_down, :combine_jobs => :script_only, :pdata_only => true}
end
end
before_filter :find_objects_by_uuid, only: :compare
include PipelineInstancesHelper
- def show
- @pipelines = [@object]
-
- if params[:compare]
- PipelineInstance.where(uuid: params[:compare]).each do |p|
- @pipelines << p
- end
- end
-
+ def graph(pipelines)
count = {}
provenance = {}
pips = {}
n = 1
- @pipelines.each do |p|
+ pipelines.each do |p|
collections = []
p.components.each do |k, v|
n = n << 1
end
+ return provenance, pips
+ end
+
+ def show
+ @pipelines = [@object]
+
+ if params[:compare]
+ PipelineInstance.where(uuid: params[:compare]).each do |p|
+ @pipelines << p
+ end
+ end
+
+ provenance, pips = graph(@pipelines)
+
@prov_svg = ProvenanceHelper::create_provenance_graph provenance, "provenance_svg", {
:all_script_parameters => true,
:combine_jobs => :script_and_version,
end
end
end
+
+ provenance, pips = graph(@objects)
+
+ @prov_svg = ProvenanceHelper::create_provenance_graph provenance, "provenance_svg", {
+ :all_script_parameters => true,
+ :combine_jobs => :script_and_version,
+ :script_version_nodes => true,
+ :pips => pips }
end
protected
#"\"#{uuid}\" [label=\"#{rsc}\\n#{uuid}\",href=\"#{href}\"];\n"
if rsc == Collection
+ puts uuid
+ if uuid == :"d41d8cd98f00b204e9800998ecf8427e+0"
+ # special case
+ puts "empty!"
+ return "\"#{uuid}\" [label=\"(empty collection)\"];\n"
+ end
if @pdata[uuid]
#puts @pdata[uuid]
if @pdata[uuid][:name]
sp.each do |v|
if GenerateGraph::collection_uuid(v)
gr += script_param_edges(job, "#{prefix}[#{i}]", v)
- else
+ elsif @opts[:all_script_parameters]
node += "', '" unless node == ""
node = "['" if node == ""
node += "#{v}"
end
else
m = GenerateGraph::collection_uuid(sp)
- if m
+ #puts "#{m} pdata is #{@pdata[m.intern]}"
+ if m and (@pdata[m.intern] or (not @opts[:pdata_only]))
gr += edge(job_uuid(job), m, {:label => prefix})
gr += generate_provenance_edges(m)
elsif @opts[:all_script_parameters]
# uuid is a collection
gr += describe_node(uuid)
+ if m == :"d41d8cd98f00b204e9800998ecf8427e+0"
+ # empty collection, don't follow any further
+ return gr
+ end
+
@pdata.each do |k, job|
if job[:output] == uuid.to_s
gr += edge(uuid, job_uuid(job), {:label => "output"})
end
gr = """strict digraph {
-node [fontsize=8,shape=box];
-edge [fontsize=8];
+node [fontsize=10,shape=box];
+edge [fontsize=10];
"""
if opts[:direction] == :bottom_up
def provenance
$arvados_api_client.api "collections/#{self.uuid}/", "provenance"
end
+
+ def used_by
+ $arvados_api_client.api "collections/#{self.uuid}/", "used_by"
+ end
end
<%= content_for :css do %>
+/* Need separate style for each instance of svg div because javascript will manipulate the properties. */
#<%= divId %> {
padding-left: 3px;
overflow: auto;
border-color: gray;
position: absolute;
left: 1px;
-
+}
+path:hover {
+stroke-width: 5;
}
<% end %>
-<div id="_<%= divId %>_container" style="padding-top: 41px; margin-top: -41px">
- <script>
- (function() {
+<%= content_for :js do %>
+ $(window).on('load', function() {
var fn = function () { graph_zoom("<%= divId %>", "<%=svgId %>", 1) };
- $(window).resize(fn);
- $(window).load(fn);
- $(window).scroll(fn);
- })();
- </script>
+ $(window).on('resize', fn);
+ $(window).on('scroll', fn);
+ });
+<% end %>
+
+<div id="_<%= divId %>_container" style="padding-top: 41px; margin-top: -41px">
<div style="text-align: right">
<a style="cursor: pointer"><span class="icon-zoom-out" onclick="graph_zoom('<%= divId %>', '<%= svgId %>', .9)"></span></a>
<a style="cursor: pointer"><span class="icon-zoom-in" onclick="graph_zoom('<%= divId %>', '<%= svgId %>', 1./.9)"></span></a>
</div>
<div id="<%= divId %>">
- <span id="_<%= divId %>_center" style="padding-left: 300px"/>
+ <span id="_<%= divId %>_center" style="padding-left: 300px"></span>
<%= raw(svg) %>
</div>
- <div id="_<%= divId %>_padding" style="padding-bottom: 1em" />
+ <div id="_<%= divId %>_padding" style="padding-bottom: 1em"></div>
</div>
<div class="tabbable">
<ul class="nav nav-tabs">
<% pane_list.each_with_index do |pane, i| %>
- <li class="<%= 'active' if i==0 %>"><a href="#<%= pane %>" data-toggle="tab"><%= pane.capitalize %></a></li>
+ <li class="<%= 'active' if i==0 %>"><a href="#<%= pane %>" data-toggle="tab" id="<%= pane %>-tab"> <%= pane.capitalize %></a></li>
<% end %>
</ul>
<div class="tabbable">
<ul class="nav nav-tabs">
<% pane_list.each_with_index do |pane, i| %>
- <li class="<%= 'active' if i==0 %>"><a href="#<%= pane %>" data-toggle="tab"><%= pane.capitalize.gsub '_', ' ' %></a></li>
+ <li class="<%= 'active' if i==0 %>"><a href="#<%= pane %>" data-toggle="tab" id="<%= pane %>-tab"><%= pane.capitalize.gsub '_', ' ' %></a></li>
<% end %>
</ul>
<% content_for :js do %>
+ $(window).on('load',
$(function() {
- $('#prov-tab').on('shown', function() { provenance_sizing_fixup("provenance_graph", "provenance_svg"); });
- })
+ $('#provenance_graph-tab').on('shown', function() { graph_zoom("provenance_graph_div", "provenance_svg", 1); });
+ }))
<% end %>
<%= render partial: 'application/svg_div', locals: {
- divId: "provenance_graph",
+ divId: "provenance_graph_div",
svgId: "provenance_svg",
svg: @prov_svg } %>
--- /dev/null
+ <% content_for :js do %>
+ $(window).on('load',
+ $(function() {
+ $('#used_by-tab').on('shown', function() { graph_zoom("used_by_graph", "used_by_svg", 1); });
+ }))
+<% end %>
+<%= render partial: 'application/svg_div', locals: {
+ divId: "used_by_graph",
+ svgId: "used_by_svg",
+ svg: @used_by_svg } %>
+
+++ /dev/null
-<%= content_for :css do %>
-<%# https://github.com/mbostock/d3/wiki/Ordinal-Scales %>
-<% n=-1; %w(#1f77b4 #ff7f0e #2ca02c #d62728 #9467bd #8c564b #e377c2 #7f7f7f #bcbd22 #17becf).each do |color| %>
-.colorseries-10-<%= n += 1 %>, .btn.colorseries-10-<%= n %>:hover, .label.colorseries-10-<%= n %>:hover {
- *background-color: <%= color %>;
- background-color: <%= color %>;
- background-image: none;
-}
-<% end %>
-.colorseries-nil { }
-.label a {
- color: inherit;
-}
-
-<% end %>
-
-<div class="tabbable">
-<ul class="nav nav-tabs">
- <li class="active"><a href="#files" data-toggle="tab">Files (<%= @object.files ? @object.files.size : 0 %>)</a></li>
- <li><a href="#provenance" data-toggle="tab">Provenance (<%= @provenance.size %>)</a></li>
- <li><a href="#jobs" data-toggle="tab">Jobs (<%= @provenance.size %>)</a></li>
- <li><a href="#sourcedata" data-toggle="tab">Source data (<%= @sourcedata.size %>)</a></li>
- <li><a href="#owner-groups-resources" data-toggle="tab">Owner, groups, resources</a></li>
- <li><a href="#provenance2" data-toggle="tab" id="prov-tab">Provenance graph</a></li>
- <li><a href="#used-by" data-toggle="tab">Used by</a></li>
-</ul>
-
-<div class="tab-content">
- <div id="files" class="tab-pane fade in active">
- <table class="table table-bordered" style="table-layout: fixed">
- <thead>
- <tr>
- <th>path</th>
- <th>file</th>
- <th style="width:1.5em">d/l</th>
- <th style="width: 7em; text-align:right">size</th>
- </tr>
- </thead><tbody>
- <% if @object then @object.files.sort_by{|f|f[1]}.each do |file| %>
- <% file_path = "#{file[0]}/#{file[1]}" %>
- <tr>
- <td>
- <%= file[0] %>
- </td>
-
- <td>
- <%= link_to file[1], {controller: 'collections', action: 'show_file', uuid: @object.uuid, file: file_path, size: file[2], disposition: 'inline'}, {title: 'View in browser'} %>
- </td>
-
- <td>
- <div style="display:inline-block">
- <%= link_to raw('<i class="icon-download"></i>'), {controller: 'collections', action: 'show_file', uuid: @object.uuid, file: file_path, size: file[2], disposition: 'attachment'}, {class: 'label label-info', title: 'Download'} %>
- </div>
- </td>
-
- <td style="text-align:right">
- <%= raw(human_readable_bytes_html(file[2])) %>
- </td>
-
- </tr>
- <% end; end %>
- </tbody>
- </table>
- </div>
- <div id="provenance" class="tab-pane fade">
- <table class="topalign table table-bordered">
- <thead>
- </thead>
- <tbody>
-
- <% @provenance.reverse.each do |p| %>
- <% j = p[:job] %>
-
- <% if j %>
-
- <tr class="job">
- <td style="padding-bottom: 3em">
- <table class="table" style="margin-bottom: 0; background: #f0f0ff">
- <% j.script_parameters.each do |k,v| %>
- <tr>
- <td style="width: 20%">
- <%= k.to_s %>
- </td><td style="width: 60%">
- <% if v and @output2job.has_key? v %>
- <tt class="label colorseries-10-<%= @output2colorindex[v] %>"><%= link_to_if_arvados_object v %></tt>
- <% else %>
- <span class="deemphasize"><%= link_to_if_arvados_object v %></span>
- <% end %>
- </td><td style="text-align: center; width: 20%">
- <% if v
- if @protected[v]
- labelclass = 'success'
- labeltext = 'keep'
- else
- labelclass = @output2job.has_key?(v) ? 'warning' : 'danger'
- labeltext = 'cache'
- end %>
-
- <tt class="label label-<%= labelclass %>"><%= labeltext %></tt>
- <% end %>
- </td>
- </tr>
- <% end %>
- </table>
- <div style="text-align: center">
- ↓
- <br />
- <span class="label"><%= j.script %><br /><tt><%= link_to_if j.script_version.match(/[0-9a-f]{40}/), j.script_version, "https://arvados.org/projects/arvados/repository/revisions/#{j.script_version}/entry/crunch_scripts/#{j.script}" if j.script_version %></tt></span>
- <br />
- ↓
- <br />
- <tt class="label colorseries-10-<%= @output2colorindex[p[:output]] %>"><%= link_to_if_arvados_object p[:output] %></tt>
- </div>
- </td>
- <td>
- <tt><span class="deemphasize">job:</span><br /><%= link_to_if_arvados_object j %><br /><span class="deemphasize"><%= j.submit_id %></span></tt>
- </td>
- </tr>
-
- <% else %>
- <tr>
- <td>
- <span class="label label-danger">lookup fail</span>
- <br />
- <tt class="deemphasize"><%= p[:target] %></tt>
- </td><td colspan="5">
- </td>
- </tr>
- <% end %>
-
- <% end %>
-
- </tbody>
- </table>
- </div>
- <div id="provenance2" class="tab-pane fade">
-<script>
- $(function() {
- $('#prov-tab').on('shown', function() { provenance_sizing_fixup("provenance_graph", "provenance_svg"); });
- })
-</script>
- <%= render partial: 'application/svg_div', locals: {
- divId: "provenance_graph",
- svgId: "provenance_svg",
- svg: @prov_svg } %>
- </div>
- <div id="jobs" class="tab-pane fade">
- <table class="topalign table table-bordered">
- <thead>
- <tr class="contain-align-left">
- <th>
- job
- </th><th>
- version
- </th><th>
- status
- </th><th>
- start
- </th><th>
- finish
- </th><th>
- clock time
- </th>
- </tr>
- </thead>
- <tbody>
-
- <% @provenance.reverse.each do |p| %>
- <% j = p[:job] %>
-
- <% if j %>
-
- <tr class="job">
- <td>
- <tt><%= j.uuid %></tt>
- <br />
- <tt class="deemphasize"><%= j.submit_id %></tt>
- </td><td>
- <%= j.script_version %>
- </td><td>
- <span class="label <%= if j.success then 'label-success'; elsif j.running then 'label-primary'; else 'label-warning'; end %>">
- <%= j.success || j.running ? 'ok' : 'failed' %>
- </span>
- </td><td>
- <%= j.started_at %>
- </td><td>
- <%= j.finished_at %>
- </td><td>
- <% if j.started_at and j.finished_at %>
- <%= raw(distance_of_time_in_words(j.started_at, j.finished_at).sub('about ','~').sub(' ',' ')) %>
- <% elsif j.started_at and j.running %>
- <%= raw(distance_of_time_in_words(j.started_at, Time.now).sub('about ','~').sub(' ',' ')) %> (running)
- <% end %>
- </td>
- </tr>
-
- <% else %>
- <tr>
- <td>
- <span class="label label-danger">lookup fail</span>
- <br />
- <tt class="deemphasize"><%= p[:target] %></tt>
- </td><td colspan="4">
- </td>
- </tr>
- <% end %>
-
- <% end %>
-
- </tbody>
- </table>
- </div>
- <div id="sourcedata" class="tab-pane fade">
- <table class="table table-bordered table-striped">
- <thead>
- <tr class="contain-align-left">
- <th>
- collection
- </th><th class="data-size">
- data size
- </th><th>
- storage
- </th><th>
- origin
- </th>
- </tr>
- </thead>
- <tbody>
-
- <% @sourcedata.values.each do |sourcedata| %>
-
- <tr class="collection">
- <td>
- <tt class="label"><%= sourcedata[:uuid] %></tt>
- </td><td class="data-size">
- <%= raw(human_readable_bytes_html(sourcedata[:collection].data_size)) if sourcedata[:collection] and sourcedata[:collection].data_size %>
- </td><td>
- <% if @protected[sourcedata[:uuid]] %>
- <span class="label label-success">keep</span>
- <% else %>
- <span class="label label-danger">cache</span>
- <% end %>
- </td><td>
- <% if sourcedata[:data_origins] %>
- <% sourcedata[:data_origins].each do |data_origin| %>
- <span class="deemphasize"><%= data_origin[0] %></span>
- <%= data_origin[2] %>
- <br />
- <% end %>
- <% end %>
- </td>
- </tr>
-
- <% end %>
-
- </tbody>
- </table>
- </div>
- <div id="owner-groups-resources" class="tab-pane fade">
- <%= render :partial => 'application/arvados_object' %>
- </div>
-</div>
-</div>
<div class="row" style="padding: .5em">
</div>
<% end %>
+
+
+ <div style="text-align: center">
+ <span class="pipeline_color_legend" style="background: #88ff88">This pipeline</span>
+ <span class="pipeline_color_legend" style="background: #8888ff">Comparison pipeline</span>
+ <span class="pipeline_color_legend" style="background: #88ffff">Shared by both pipelines</span>
+ </div>
+
+ <%= render partial: 'application/svg_div', locals: {
+ divId: "provenance_graph",
+ svgId: "provenance_svg",
+ svg: @prov_svg } %>
conditions << nil
elsif value.is_a? Array
if value[0] == 'contains' and value.length == 2
- conditions[0] << "and #{table_name}.#{attr} ilike ?"
+ conditions[0] << " and #{table_name}.#{attr} like ?"
conditions << "%#{value[1]}%"
else
conditions[0] << " and #{table_name}.#{attr} in (?)"
render json: visited
end
+ def generate_used_by_edges(visited, uuid)
+ m = collection_uuid(uuid)
+ uuid = m if m
+
+ if not uuid or uuid.empty? or visited[uuid]
+ return ""
+ end
+
+ logger.debug "visiting #{uuid}"
+
+ if m
+ # uuid is a collection
+ Collection.readable_by(current_user).where(uuid: uuid).each do |c|
+ visited[uuid] = c.as_api_response
+ visited[uuid][:files] = []
+ c.files.each do |f|
+ visited[uuid][:files] << f
+ end
+ end
+
+ if uuid == "d41d8cd98f00b204e9800998ecf8427e+0"
+ # special case for empty collection
+ return
+ end
+
+ Job.readable_by(current_user).where(["jobs.script_parameters like ?", "%#{uuid}%"]).each do |job|
+ generate_used_by_edges(visited, job.uuid)
+ end
+
+ else
+ # uuid is something else
+ rsc = ArvadosModel::resource_class_for_uuid uuid
+ if rsc == Job
+ Job.readable_by(current_user).where(uuid: uuid).each do |job|
+ visited[uuid] = job.as_api_response
+ generate_used_by_edges(visited, job.output)
+ end
+ elsif rsc != nil
+ rsc.where(uuid: uuid).each do |r|
+ visited[uuid] = r.as_api_response
+ end
+ end
+ end
+
+ Link.readable_by(current_user).
+ where(tail_uuid: uuid, link_class: "provenance").
+ each do |link|
+ visited[link.uuid] = link.as_api_response
+ generate_used_by_edges(visited, link.head_uuid)
+ end
+
+ #puts "finished #{uuid}"
+ end
+
+ def used_by
+ visited = {}
+ generate_used_by_edges(visited, @object[:uuid])
+ render json: visited
+ end
protected
def find_object_by_uuid
get '/user_agreements/signatures' => 'user_agreements#signatures'
post '/user_agreements/sign' => 'user_agreements#sign'
get '/collections/:uuid/provenance' => 'collections#provenance'
+ get '/collections/:uuid/used_by' => 'collections#used_by'
resources :collections
resources :links
resources :nodes