Path: blob/main/_plugins/gtn/supported.rb
1677 views
# frozen_string_literal: true12require './_plugins/gtn/usegalaxy'34module Gtn5# Handle tool support queries6module Supported7##8# Identify the servers that support a given tool list9#10# Params:11# +data+:: The data from metadata/public-server-tools.json12# +tool_list+:: The list of tools to check (either 'upload1' or13# 'toolshed.g2.bx.psu.edu/repos/iuc/circos/circos/0.69.8+galaxy10' style tools)14# Returns:15# +supported+:: A hash of supported servers, with the following structure:16# {17# exact: [server1, server2, ...],18# inexact: [server1, server2, ...]19# }20def self.calculate(data, tool_list)21# p "Calculating supported servers for this tool list"22if data.nil? || data.empty? || tool_list.empty? || tool_list.nil?23return {24'exact' => [],25'inexact' => Gtn::Usegalaxy.servers.map do |x|26x = x.transform_keys(&:to_s)27x['usegalaxy'] = true28x29end30}31end3233supported = { exact: {}, inexact: {} }34tool_list.each do |tool|35if tool.count('/') > 436# E.g. toolshed.g2.bx.psu.edu/repos/iuc/circos/circos/0.69.8+galaxy1037tool_id = tool.split('/')[0..4].join('/')38tool_version = tool.split('/')[5..].join('/')39# p "Checking #{tool_id} #{tool_version}... "4041if data['tools'].key?(tool_id)42supported[:exact][tool] = data['tools'][tool_id][tool_version] if data['tools'][tool_id].key?(tool_version)4344supported[:inexact][tool] = data['tools'][tool_id].map { |_, v| v }.flatten.uniq.sort45end46elsif data['tools'].key?(tool)47# E.g. 'upload1'48# p "Checking #{tool}... "49supported[:inexact][tool] = data['tools'][tool].map { |_, v| v }.flatten.uniq.sort50supported[:exact][tool] = data['tools'][tool].map { |_, v| v }.flatten.uniq.sort51end52end5354# Exactly supporting servers:55# this is the set of intersections across supported[:exact][*]56exact_support = (0..data['servers'].length - 1).to_a57tool_list.each do |tool|58exact_support &= (supported[:exact][tool] || [])59end6061# Inexactly supporting servers62# Set of intersections across (union of supported[:exact] and supported[:inexact])63inexact_support = (0..data['servers'].length - 1).to_a64tool_list.each do |tool|65et = supported[:exact][tool] || []66it = supported[:inexact][tool] || []67inexact_support &= (et | it)68end69# Remove the exactly supported ones because that would be extraneous we70# check here if it's an array because the above code will occasionally71# generate a 'false' value when merging sets.72inexact_support -= exact_support7374usegalaxy_server_urls = Gtn::Usegalaxy.servers.map { |x| x[:url].downcase.gsub(/\/$/, '')}7576{77'exact' => (exact_support || []).map do |id|78data['servers'][id].update(79{ 'usegalaxy' => usegalaxy_server_urls.include?(data['servers'][id]['url'].downcase.gsub(/\/$/, '')) }80)81end,82'inexact' => (inexact_support || []).map do |id|83data['servers'][id].update(84{ 'usegalaxy' => usegalaxy_server_urls.include?(data['servers'][id]['url'].downcase.gsub(/\/$/, '')) }85)86end87}88end8990##91# Identify the servers that support a given tool list92#93# Params:94# +data+:: The data from metadata/public-server-tools.json95# +tool_list+:: The list of tools to check (either 'upload1' or96# 'toolshed.g2.bx.psu.edu/repos/iuc/circos/circos/0.69.8+galaxy10' style tools)97# Returns:98# +supported+:: A hash of supported servers, with the following structure:99# {100# servers: [a, b, c]101# tools: [102# {103# id: toolshed.g2.bx.psu.edu/repos/iuc/circos/circos/0.69.8+galaxy10,104# version: 0.69.8+galaxy10,105# servers: [106# [0.69.8+galaxy10],107# [0.69.8+galaxy11, 0.69.8+galaxy12],108# nil109# ]110# }111# ]112# }113def self.calculate_matrix(data, tool_list)114structure = {115'servers' => [],116'tools' => [],117}118# p "Calculating supported servers for this tool list"119if data.nil? || data.empty? || tool_list.empty? || tool_list.nil?120return structure121end122123structure['servers'] = data['servers']124125tool_list.each do |tool|126tool_for_server = {127'id' => tool,128'servers' => []129}130131if tool.count('/') > 4132# E.g. toolshed.g2.bx.psu.edu/repos/iuc/circos/circos/0.69.8+galaxy10133tool_id = tool.split('/')[0..4].join('/')134tool_version = tool.split('/')[5..].join('/')135tool_for_server['version'] = tool_version136else137tool_id = tool138tool_version = 'local'139tool_for_server['version'] = tool_version140end141142data['servers'].each.with_index do |server, s_index|143# If we've never seen this tool anywhere144if ! data['tools'].key?(tool_id)145res = {'state' => 'missing'}146else147# If we've seen this exact version on the current server/index148if data['tools'][tool_id].key?(tool_version) && data['tools'][tool_id][tool_version].include?(s_index)149res = {'state' => 'exact', 'version' => tool_version}150elsif data['tools'][tool_id]151res = {152'state' => 'inexact',153'versions' => data['tools'][tool_id].select{|k, v| v.include?(s_index)}.keys154}155156if res['versions'].length == 1 && res['versions'][0] == '_'157res = {158'state' => 'local'159}160elsif res['versions'].length == 0161res = {'state' => 'missing'}162end163end164end165166res['server'] = server['url']167tool_for_server['servers'].push(res)168169end170171structure['tools'].push(tool_for_server)172end173174# TODO: would be nice to re-sort the servers by the number of exact matches175structure176end177end178end179180## Allow running from the CLI181if __FILE__ == $PROGRAM_NAME182if ARGV.length.positive? && (ARGV[0] == 'test')183require 'test/unit'184# Testing for the class185class Gtn::Test::IntersectionTest < Test::Unit::TestCase186def test_exact187data = {188'servers' => { 0 => 's0', 1 => 's1', 2 => 's2' },189'tools' => {190'upload1' => {191'_' => [0, 1, 2]192},193'ts/repos/hexy/lena/tool1' => {194'1.0' => [0, 1],195'2.0' => [0, 2]196}197}198}199200# Inexact here is empty because all are exactly supporting201assert_equal(Gtn::Supported.calculate(data, ['upload1']), { exact: %w[s0 s1 s2], inexact: [] })202assert_equal(Gtn::Supported.calculate(data, ['upload1', 'ts/repos/hexy/lena/tool1/1.0']),203{ exact: %w[s0 s1], inexact: %w[s2] })204assert_equal(Gtn::Supported.calculate(data, ['upload1', 'ts/repos/hexy/lena/tool1/2.0']),205{ exact: %w[s0 s2], inexact: %w[s1] })206assert_equal(Gtn::Supported.calculate(data, %w[upload1 unknown-tool]),207{ exact: %w[], inexact: %w[] })208end209end210else211server = ARGV[0]212workflow = ARGV[1]213214def short_id(tool_id)215if tool_id.count('/') > 4216tool_id.split('/')[0..-2].join('/')217else218tool_id219end220end221222require 'json'223tool_list = JSON.parse(`curl -s #{server}/api/tools?in_panel=False`).map{|x| x['id']}224puts "Found #{tool_list.length} tools in #{server}"225226version_smash = tool_list.map{|x|227if x.count('/') > 4228[x.split('/')[0..-2].join('/'), x.split('/')[-1]]229else230[x, []]231end232}233version_smash = version_smash.group_by{|x, y| x}.to_a.map{|k, v| [k, v.map{|x, y| y}]}.to_h234235workflow_tools = JSON.parse(File.open(workflow).read)['steps'].map{|_, x| x['tool_id']}236workflow_tools.select!{|x| x && x.length.positive?}237puts "Found #{workflow_tools.length} tools in the #{workflow}"238239workflow_tools.each do |tool|240if tool_list.include?(tool)241puts "✅ #{tool} is supported"242else243if version_smash.key?(short_id(tool))244puts "❔ #{tool} is not supported, but #{version_smash[short_id(tool)]} are"245else246puts "❌ #{tool} is not supported"247end248end249end250251metadata = {252"servers" => [{"url" => server, "name" => "CLI Tested Server"}],253"tools" => {}254}255version_smash.each do |k, v|256if k.count('/') > 3257metadata["tools"][k] = v.map{|x| [x, [0]]}.to_h258else259metadata["tools"][k] = {260"_" => [0]261}262end263end264265puts "GTN Rendering:"266pp Gtn::Supported.calculate(metadata, workflow_tools)267end268end269270271