Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
galaxyproject
GitHub Repository: galaxyproject/training-material
Path: blob/main/_plugins/gtn/supported.rb
1677 views
1
# frozen_string_literal: true
2
3
require './_plugins/gtn/usegalaxy'
4
5
module Gtn
6
# Handle tool support queries
7
module Supported
8
##
9
# Identify the servers that support a given tool list
10
#
11
# Params:
12
# +data+:: The data from metadata/public-server-tools.json
13
# +tool_list+:: The list of tools to check (either 'upload1' or
14
# 'toolshed.g2.bx.psu.edu/repos/iuc/circos/circos/0.69.8+galaxy10' style tools)
15
# Returns:
16
# +supported+:: A hash of supported servers, with the following structure:
17
# {
18
# exact: [server1, server2, ...],
19
# inexact: [server1, server2, ...]
20
# }
21
def self.calculate(data, tool_list)
22
# p "Calculating supported servers for this tool list"
23
if data.nil? || data.empty? || tool_list.empty? || tool_list.nil?
24
return {
25
'exact' => [],
26
'inexact' => Gtn::Usegalaxy.servers.map do |x|
27
x = x.transform_keys(&:to_s)
28
x['usegalaxy'] = true
29
x
30
end
31
}
32
end
33
34
supported = { exact: {}, inexact: {} }
35
tool_list.each do |tool|
36
if tool.count('/') > 4
37
# E.g. toolshed.g2.bx.psu.edu/repos/iuc/circos/circos/0.69.8+galaxy10
38
tool_id = tool.split('/')[0..4].join('/')
39
tool_version = tool.split('/')[5..].join('/')
40
# p "Checking #{tool_id} #{tool_version}... "
41
42
if data['tools'].key?(tool_id)
43
supported[:exact][tool] = data['tools'][tool_id][tool_version] if data['tools'][tool_id].key?(tool_version)
44
45
supported[:inexact][tool] = data['tools'][tool_id].map { |_, v| v }.flatten.uniq.sort
46
end
47
elsif data['tools'].key?(tool)
48
# E.g. 'upload1'
49
# p "Checking #{tool}... "
50
supported[:inexact][tool] = data['tools'][tool].map { |_, v| v }.flatten.uniq.sort
51
supported[:exact][tool] = data['tools'][tool].map { |_, v| v }.flatten.uniq.sort
52
end
53
end
54
55
# Exactly supporting servers:
56
# this is the set of intersections across supported[:exact][*]
57
exact_support = (0..data['servers'].length - 1).to_a
58
tool_list.each do |tool|
59
exact_support &= (supported[:exact][tool] || [])
60
end
61
62
# Inexactly supporting servers
63
# Set of intersections across (union of supported[:exact] and supported[:inexact])
64
inexact_support = (0..data['servers'].length - 1).to_a
65
tool_list.each do |tool|
66
et = supported[:exact][tool] || []
67
it = supported[:inexact][tool] || []
68
inexact_support &= (et | it)
69
end
70
# Remove the exactly supported ones because that would be extraneous we
71
# check here if it's an array because the above code will occasionally
72
# generate a 'false' value when merging sets.
73
inexact_support -= exact_support
74
75
usegalaxy_server_urls = Gtn::Usegalaxy.servers.map { |x| x[:url].downcase.gsub(/\/$/, '')}
76
77
{
78
'exact' => (exact_support || []).map do |id|
79
data['servers'][id].update(
80
{ 'usegalaxy' => usegalaxy_server_urls.include?(data['servers'][id]['url'].downcase.gsub(/\/$/, '')) }
81
)
82
end,
83
'inexact' => (inexact_support || []).map do |id|
84
data['servers'][id].update(
85
{ 'usegalaxy' => usegalaxy_server_urls.include?(data['servers'][id]['url'].downcase.gsub(/\/$/, '')) }
86
)
87
end
88
}
89
end
90
91
##
92
# Identify the servers that support a given tool list
93
#
94
# Params:
95
# +data+:: The data from metadata/public-server-tools.json
96
# +tool_list+:: The list of tools to check (either 'upload1' or
97
# 'toolshed.g2.bx.psu.edu/repos/iuc/circos/circos/0.69.8+galaxy10' style tools)
98
# Returns:
99
# +supported+:: A hash of supported servers, with the following structure:
100
# {
101
# servers: [a, b, c]
102
# tools: [
103
# {
104
# id: toolshed.g2.bx.psu.edu/repos/iuc/circos/circos/0.69.8+galaxy10,
105
# version: 0.69.8+galaxy10,
106
# servers: [
107
# [0.69.8+galaxy10],
108
# [0.69.8+galaxy11, 0.69.8+galaxy12],
109
# nil
110
# ]
111
# }
112
# ]
113
# }
114
def self.calculate_matrix(data, tool_list)
115
structure = {
116
'servers' => [],
117
'tools' => [],
118
}
119
# p "Calculating supported servers for this tool list"
120
if data.nil? || data.empty? || tool_list.empty? || tool_list.nil?
121
return structure
122
end
123
124
structure['servers'] = data['servers']
125
126
tool_list.each do |tool|
127
tool_for_server = {
128
'id' => tool,
129
'servers' => []
130
}
131
132
if tool.count('/') > 4
133
# E.g. toolshed.g2.bx.psu.edu/repos/iuc/circos/circos/0.69.8+galaxy10
134
tool_id = tool.split('/')[0..4].join('/')
135
tool_version = tool.split('/')[5..].join('/')
136
tool_for_server['version'] = tool_version
137
else
138
tool_id = tool
139
tool_version = 'local'
140
tool_for_server['version'] = tool_version
141
end
142
143
data['servers'].each.with_index do |server, s_index|
144
# If we've never seen this tool anywhere
145
if ! data['tools'].key?(tool_id)
146
res = {'state' => 'missing'}
147
else
148
# If we've seen this exact version on the current server/index
149
if data['tools'][tool_id].key?(tool_version) && data['tools'][tool_id][tool_version].include?(s_index)
150
res = {'state' => 'exact', 'version' => tool_version}
151
elsif data['tools'][tool_id]
152
res = {
153
'state' => 'inexact',
154
'versions' => data['tools'][tool_id].select{|k, v| v.include?(s_index)}.keys
155
}
156
157
if res['versions'].length == 1 && res['versions'][0] == '_'
158
res = {
159
'state' => 'local'
160
}
161
elsif res['versions'].length == 0
162
res = {'state' => 'missing'}
163
end
164
end
165
end
166
167
res['server'] = server['url']
168
tool_for_server['servers'].push(res)
169
170
end
171
172
structure['tools'].push(tool_for_server)
173
end
174
175
# TODO: would be nice to re-sort the servers by the number of exact matches
176
structure
177
end
178
end
179
end
180
181
## Allow running from the CLI
182
if __FILE__ == $PROGRAM_NAME
183
if ARGV.length.positive? && (ARGV[0] == 'test')
184
require 'test/unit'
185
# Testing for the class
186
class Gtn::Test::IntersectionTest < Test::Unit::TestCase
187
def test_exact
188
data = {
189
'servers' => { 0 => 's0', 1 => 's1', 2 => 's2' },
190
'tools' => {
191
'upload1' => {
192
'_' => [0, 1, 2]
193
},
194
'ts/repos/hexy/lena/tool1' => {
195
'1.0' => [0, 1],
196
'2.0' => [0, 2]
197
}
198
}
199
}
200
201
# Inexact here is empty because all are exactly supporting
202
assert_equal(Gtn::Supported.calculate(data, ['upload1']), { exact: %w[s0 s1 s2], inexact: [] })
203
assert_equal(Gtn::Supported.calculate(data, ['upload1', 'ts/repos/hexy/lena/tool1/1.0']),
204
{ exact: %w[s0 s1], inexact: %w[s2] })
205
assert_equal(Gtn::Supported.calculate(data, ['upload1', 'ts/repos/hexy/lena/tool1/2.0']),
206
{ exact: %w[s0 s2], inexact: %w[s1] })
207
assert_equal(Gtn::Supported.calculate(data, %w[upload1 unknown-tool]),
208
{ exact: %w[], inexact: %w[] })
209
end
210
end
211
else
212
server = ARGV[0]
213
workflow = ARGV[1]
214
215
def short_id(tool_id)
216
if tool_id.count('/') > 4
217
tool_id.split('/')[0..-2].join('/')
218
else
219
tool_id
220
end
221
end
222
223
require 'json'
224
tool_list = JSON.parse(`curl -s #{server}/api/tools?in_panel=False`).map{|x| x['id']}
225
puts "Found #{tool_list.length} tools in #{server}"
226
227
version_smash = tool_list.map{|x|
228
if x.count('/') > 4
229
[x.split('/')[0..-2].join('/'), x.split('/')[-1]]
230
else
231
[x, []]
232
end
233
}
234
version_smash = version_smash.group_by{|x, y| x}.to_a.map{|k, v| [k, v.map{|x, y| y}]}.to_h
235
236
workflow_tools = JSON.parse(File.open(workflow).read)['steps'].map{|_, x| x['tool_id']}
237
workflow_tools.select!{|x| x && x.length.positive?}
238
puts "Found #{workflow_tools.length} tools in the #{workflow}"
239
240
workflow_tools.each do |tool|
241
if tool_list.include?(tool)
242
puts "✅ #{tool} is supported"
243
else
244
if version_smash.key?(short_id(tool))
245
puts "❔ #{tool} is not supported, but #{version_smash[short_id(tool)]} are"
246
else
247
puts "❌ #{tool} is not supported"
248
end
249
end
250
end
251
252
metadata = {
253
"servers" => [{"url" => server, "name" => "CLI Tested Server"}],
254
"tools" => {}
255
}
256
version_smash.each do |k, v|
257
if k.count('/') > 3
258
metadata["tools"][k] = v.map{|x| [x, [0]]}.to_h
259
else
260
metadata["tools"][k] = {
261
"_" => [0]
262
}
263
end
264
end
265
266
puts "GTN Rendering:"
267
pp Gtn::Supported.calculate(metadata, workflow_tools)
268
end
269
end
270
271