class Object
Constants
- APPROVED_VOICES
- ARI_MAP
- BASE_REF
- CONTRIBUTORS
Full Contributors Data
- CONTRIBUTORS_SCHEMA
- CONTRIBUTORS_SCHEMA_UNSAFE
Any error messages
- END_OF_SENTENCE_DURATION
- END_OF_SLIDE_DURATION
- FASTIMAGE_AVAILABLE
The fastimage gem may not be installed, and that is OK, we will fall back to another method
- FUNDERS
- FUNDERS_SCHEMA
- FUNDERS_SCHEMA_UNSAFE
- GALAXIES
- GTN_CACHE
- NOW
modifiedfiles = `git diff –cached –name-only –ignore-all-space –diff-filter=M #{options}`.split(“n”)
- ORGANISATIONS
- ORGANISATIONS_SCHEMA
- ORGANISATIONS_SCHEMA_UNSAFE
- PUNCTUATION
- TEST_PHRASES
- TITLE_TOPIC
- TUTO_ID
- WORD_MAP
Public Instance Methods
automagic_loading(f)
click to toggle source
# File bin/gtn.rb, line 7 def automagic_loading(f) # Remove our documentation f.reject! { |k, v| k == 'description' and v.is_a?(String) } f.reject! { |k| k == 'examples' } # Auto-replace CONTRIBUTORS in enums. f.each do |k, v| if v.is_a?(Hash) automagic_loading(v) elsif v.is_a?(Array) if k == 'enum' repl = [] # If one of the elements in this array is CONTRIBUTORS, replace it with the same named variable repl << CONTRIBUTORS.keys if v.find { |x| x == 'CONTRIBUTORS' } repl << FUNDERS.keys if v.find { |x| x == 'FUNDERS' } repl << ORGANISATIONS.keys if v.find { |x| x == 'ORGANISATIONS' } v.replace repl.flatten if repl.length.positive? end v.flatten.each { |x| automagic_loading(x) if x.is_a?(Hash) } end end f end
build_news(data, filter: nil, updates: true, only_news: false)
click to toggle source
# File bin/news.rb, line 159 def build_news(data, filter: nil, updates: true, only_news: false) infix = filter.nil? ? '' : titleize(filter) output = "# GTN #{infix} News for #{NOW.strftime('%b %d')}" newsworthy = false if filter.nil? output += format_news(data[:added][:news]) newsworthy |= format_news(data[:added][:news]).length.positive? end if only_news return [output, newsworthy] end o = format_tutorials( data[:added][:tutorials].select { |n| filter.nil? || n[:path] =~ %r{topics/#{filter}} }, data[:modified][:tutorials].select { |n| filter.nil? || n[:path] =~ %r{topics/#{filter}} }, updates: updates ) output += o newsworthy |= o.length.positive? o = format_tutorials( data[:added][:slides].select { |n| filter.nil? || n[:path] =~ %r{topics/#{filter}} }, data[:modified][:slides].select { |n| filter.nil? || n[:path] =~ %r{topics/#{filter}} }, kind: 'slides', updates: updates ) output += o newsworthy |= o.length.positive? if filter.nil? && data[:contributors].length.positive? newsworthy = true output += "\n\n## #{data[:contributors].length} new contributors!\n\n" output += data[:contributors].map { |c| linkify("@#{c}", "hall-of-fame/#{c}") }.join("\n").gsub(/^/, '- ') end if filter.nil? && data[:organisations].length.positive? newsworthy = true output += "\n\n## #{data[:organisations].length} new organisations!\n\n" output += data[:organisations].map { |c| linkify("@#{c}", "hall-of-fame/#{c}") }.join("\n").gsub(/^/, '- ') end if filter.nil? && data[:funders].length.positive? newsworthy = true output += "\n\n## #{data[:funders].length} new funders!\n\n" output += data[:funders].map { |c| linkify("@#{c}", "hall-of-fame/#{c}") }.join("\n").gsub(/^/, '- ') end [output, newsworthy] end
call_engine(engine, line, mp3, voice, lang, neural)
click to toggle source
# File bin/ari-synthesize.rb, line 60 def call_engine(engine, line, mp3, voice, lang, neural) if engine == 'aws' awseng = if neural 'neural' else 'standard' end # Synthesize args = ['aws', 'polly', 'synthesize-speech', '--engine', awseng, '--language-code', lang, '--voice-id', voice, '--output-format', 'mp3', '--text', line, mp3] _, stderr, err = Open3.capture3(*args) if err.exited? && err.exitstatus.positive? puts "ERROR: #{stderr}" puts "ERROR: #{err}" exit 1 end elsif engine == 'mozilla' raw = Tempfile.new('synth-raw') _, stderr, err = Open3.capture3('curl', '--silent', '-G', '--output', raw.path, "http://localhost:5002/api/tts?text=#{CGI.escape(line)}") if err.exited? && err.exitstatus.positive? puts "ERROR: #{stderr}" exit 1 end _, stderr, err = Open3.capture3('ffmpeg', '-loglevel', 'error', '-i', raw.path, '-y', mp3) if err.exited? && err.exitstatus.positive? puts "ERROR: #{stderr}" exit 1 end end end
check_indent(file)
click to toggle source
# File bin/check-indent.rb, line 5 def check_indent(file) doc = Nokogiri::HTML(File.open(file)) # Find all <pre> tags # <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> ec = false doc.css('div.language-plaintext.highlighter-rouge div.highlight pre.highlight code').each do |pre| # Get the text content of the <pre> tag content = pre.text # Split the content by newlines lines = content.split("\n") # If all lines look like URLs: if lines.all? { |line| line =~ %r{://} } # If any are space indented lines.each do |line| if line =~ /^\s+/ puts "#{file}: Indentation error: #{line}" ec = true end end end end ec end
correct(uncorrected_line)
click to toggle source
# File bin/ari-synthesize.rb, line 48 def correct(uncorrected_line) # First we try and catch the things we can directly replace (esp usegalaxy.*) line = uncorrected_line.strip.split.map do |w| translate(w) end.join(' ') # Now we do more fancy replacements line.strip.split(/([ ‘’,'".:;!`()])/).reject(&:empty?).compact.map do |w| translate(w) end.join end
data_library_for_tutorial(path)
click to toggle source
# File bin/update-data-library, line 139 def data_library_for_tutorial(path) File.join(File.dirname(path), 'data-library.yaml') end
fetch_toolcats(server)
click to toggle source
Get the list of toolcats
# File bin/fetch-categories.rb, line 8 def fetch_toolcats(server) uri = URI.parse(server.to_s) request = Net::HTTP::Get.new(uri) req_options = { use_ssl: uri.scheme == 'https', } response = Net::HTTP.start(uri.hostname, uri.port, req_options) do |http| http.request(request) end begin JSON.parse(response.body) do |w| w end rescue StandardError {} end end
fetch_workflowhub()
click to toggle source
# File bin/workflows-fetch.rb, line 39 def fetch_workflowhub projects = JSON.parse(request('https://workflowhub.eu/projects').body) project_mapping = projects['data'].to_h { |p| [p['id'], p['attributes']['title']] } response = request('https://workflowhub.eu/workflows?filter[workflow_type]=galaxy') data = JSON.parse(response.body) if !data['links']['next'].nil? puts 'ERROR: Cannot yet handle multiple pages' exit 42 end puts "INFO: Fetching #{data['data'].length} workflows from WorkflowHub" data['data'].map.with_index do |w, _i| # {"id"=>"14", "type"=>"workflows", "attributes"=>{"title"=>"Cheminformatics - Docking"}, "links"=>{"self"=>"/workflows/14"}} wf_info = JSON.parse(request("https://workflowhub.eu#{w['links']['self']}").body) creator_list = [] creator0 = wf_info['data']['attributes']['creators'][0] if creator0.nil? # Other creators other = wf_info['data']['attributes']['other_creators'] if !other.nil? && other.length.positive? creator_list.push(wf_info['data']['attributes']['other_creators'].split(',').map(&:strip)) end else # Primary creator_list.push("#{creator0['given_name']} #{creator0['family_name']}") end # Projects wf_info['data']['relationships']['projects']['data'].each do |p| creator_list.push(project_mapping[p['id']]) end creator_list = creator_list.flatten.compact.uniq begin r = { 'name' => wf_info['data']['attributes']['title'], 'owner' => creator_list.join(', '), 'number_of_steps' => wf_info['data']['attributes']['internals']['steps'].length, 'server' => 'https://workflowhub.eu', 'id' => wf_info['data']['id'], 'tags' => wf_info['data']['attributes']['tags'].map { |t| t.gsub(/^name:/, '') }, 'update_time' => wf_info['data']['attributes']['updated_at'], } rescue StandardError r = nil end r end.compact end
fetch_workflows(server)
click to toggle source
Get the list of workflows
# File bin/workflows-fetch.rb, line 21 def fetch_workflows(server) begin response = request("#{server}/api/workflows/") rescue StandardError puts "ERROR: Failed to fetch workflows from #{server}" return [] end begin JSON.parse(response.body).map do |w| w['server'] = server w end rescue StandardError [] end end
filterSlides(x)
click to toggle source
# File bin/news.rb, line 68 def filterSlides(x) x =~ %r{topics/.*/tutorials/.*/slides.*\.html} end
filterTutorials(x)
click to toggle source
new news new slidevideos new contributors Done new tutorials Done new slides Done
# File bin/news.rb, line 64 def filterTutorials(x) x =~ %r{topics/.*/tutorials/.*/tutorial.*\.md} end
find_duration(mp3)
click to toggle source
# File bin/ari-synthesize.rb, line 94 def find_duration(mp3) stdout, = Open3.capture2('ffprobe', '-loglevel', 'error', '-show_format', '-show_streams', '-print_format', 'json', '-i', mp3) data = JSON.parse(stdout) data['format']['duration'].to_f end
fixNews(n)
click to toggle source
# File bin/news.rb, line 93 def fixNews(n) # news/_posts/2021-11-10-api.html => news/2021/11/10/api.html n[:md].gsub(%r{news/_posts/(....)-(..)-(..)-(.*.html)}, 'news/\1/\2/\3/\4') end
format_news(news)
click to toggle source
# File bin/news.rb, line 132 def format_news(news) output = '' if news.length.positive? output += "\n\n## Big News!\n\n" output += news.join("\n").gsub(/^/, '- ') end output end
format_tutorials(added, modified, kind: 'tutorials', updates: true)
click to toggle source
# File bin/news.rb, line 141 def format_tutorials(added, modified, kind: 'tutorials', updates: true) output = '' count = added.length count += modified.length if updates output += "\n\n## #{count} #{kind}!" if count.positive? if added.length.positive? output += "\n\nNew #{kind}:\n\n" output += added.map { |n| n[:md] }.join("\n").gsub(/^/, '- ') end if updates && modified.length.positive? output += "\n\nUpdated #{kind}:\n\n" output += modified.map { |n| n[:md] }.join("\n").gsub(/^/, '- ') end output end
generate_topic_feeds(site)
click to toggle source
# File _plugins/feeds.rb, line 6 def generate_topic_feeds(site) TopicFilter.list_topics(site).each do |topic| feed_path = File.join(site.dest, 'topics', topic, 'feed.xml') Jekyll.logger.debug "Generating feed for #{topic} => #{feed_path}" topic_pages = site.pages .select { |x| x.path =~ %r{^\.?/?topics/#{topic}} } .select { |x| x.path =~ %r{(tutorial.md|slides.html|faqs/.*.md)} } .reject { |x| x.path =~ /index.md/ } .reject { |x| x.data.fetch('draft', '').to_s == 'true' } .reject { |x| x.url =~ /slides-plain.html/ } .reject { |x| File.symlink?(x.path) } # Remove symlinks to other faqs/tutorials .uniq(&:path) .sort_by { |page| Gtn::PublicationTimes.obtain_time(page.path) } .reverse if topic_pages.empty? Jekyll.logger.warn "No pages for #{topic}" next else Jekyll.logger.debug "Found #{topic_pages.length} pages for #{topic}" end builder = Nokogiri::XML::Builder.new(encoding: 'UTF-8') do |xml| # Set stylesheet xml.feed(xmlns: 'http://www.w3.org/2005/Atom') do # Set generator also needs a URI attribute xml.generator('Jekyll', uri: 'https://jekyllrb.com/') xml.link(href: "#{site.config['url']}#{site.baseurl}/topics/#{topic}/feed.xml", rel: 'self') xml.updated(Gtn::ModificationTimes.obtain_time(topic_pages.first.path).to_datetime.rfc3339) xml.id("#{site.config['url']}#{site.baseurl}/topics/#{topic}/feed.xml") topic_title = site.data[topic]['title'] xml.title("Galaxy Training Network - #{topic_title}") xml.subtitle("Recently added tutorials, slides, and FAQs in the #{topic} topic") topic_pages.each do |page| page_type = if page.path =~ %r{faqs/.*.md} 'faq' else page.path.split('/').last.split('.').first end xml.entry do xml.title(page.data['title']) link = "#{site.config['url']}#{site.baseurl}#{page.url}" xml.link(href: link) # Our links are stable xml.id(link) # This is a feed of only NEW tutorials, so we only include publication times. # xml.published(Gtn::PublicationTimes.obtain_time(page.path).to_datetime.rfc3339) xml.updated(Gtn::PublicationTimes.obtain_time(page.path).to_datetime.rfc3339) # xml.path(page.path) xml.category(term: "new #{page_type}") # xml.content(page.content, type: "html") xml.summary(page.content.strip.split("\n").first, type: 'html') Gtn::Contributors.get_authors(page.data).each do |c| xml.author do xml.name(Gtn::Contributors.fetch_name(site, c)) xml.uri("#{site.config['url']}#{site.baseurl}/hall-of-fame/#{c}/") end end Gtn::Contributors.get_non_authors(page.data).each do |c| xml.contributor do xml.name(Gtn::Contributors.fetch_name(site, c)) xml.uri("#{site.config['url']}#{site.baseurl}/hall-of-fame/#{c}/") end end end end end end # The builder won't let you add a processing instruction, so we have to # serialise it to a string and then parse it again. Ridiculous. finalised = Nokogiri::XML builder.to_xml pi = Nokogiri::XML::ProcessingInstruction.new( finalised, 'xml-stylesheet', %(type="text/xml" href="#{site.config['url']}#{site.baseurl}/feed.xslt.xml") ) finalised.root.add_previous_sibling pi File.write(feed_path, finalised.to_xml) end nil end
json_boxify(h, page)
click to toggle source
# File _plugins/notebook-jupyter.rb, line 6 def json_boxify(h, page) h['cells'].each do |cell| # If it's a list, loop if cell['source'].is_a? Array cell['source'].each do |line| # rubocop:disable Layout/LineLength line.gsub!(%r{<(?<boxclass>#{Gtn::Boxify.box_classes})-title( ?(?<noprefix>noprefix))>(?<title>.*?)</\s*\k<boxclass>-title\s*>}) do # rubocop:enable Layout/LineLength m = Regexp.last_match box_type = m[:boxclass] title = m[:title] noprefix = m[:noprefix] _, box = Gtn::Boxify.generate_title(box_type, title, lang, page.path, noprefix: noprefix) box end end else # rubocop:disable Layout/LineLength cell['source'].gsub!(%r{<(?<boxclass>#{Gtn::Boxify.box_classes})-title(?<noprefix>\s+noprefix)?>(?<title>.*?)</\s*\k<boxclass>-title\s*>}) do # rubocop:enable Layout/LineLength m = Regexp.last_match box_type = m[:boxclass] title = m[:title] noprefix = m[:noprefix] _, box = Gtn::Boxify.generate_title(box_type, title, 'en', page.path, noprefix: noprefix) box end end end h end
jupyter_post_write(site)
click to toggle source
# File _plugins/notebook-jupyter.rb, line 120 def jupyter_post_write(site) site.config['__rendered_notebook_cache'].each do |_path, info| # Create if missing FileUtils.mkdir_p(info['dir']) # Write it out! File.write(info['path1'], info['content1']) File.write(info['path2'], info['content2']) end end
jupyter_pre_render(site)
click to toggle source
# File _plugins/notebook-jupyter.rb, line 38 def jupyter_pre_render(site) Jekyll.logger.info '[GTN/Notebooks] Rendering' site.config['__rendered_notebook_cache'] = {} # For every tutorial with the 'notebook' key in the page data site.pages.select { |page| GTNNotebooks.notebook_filter(page.data) }.each do |page| # We get the path to the tutorial source dir = File.dirname(File.join('.', page.url)) fn = File.join('.', page.url).sub(/html$/, 'md') notebook_language = page.data['notebook'].fetch('language', 'python') # Tag our source page page.data['tags'] = page.data['tags'] || [] page.data['tags'].push('jupyter-notebook') Jekyll.logger.info "[GTN/Notebooks] Rendering #{notebook_language} #{fn}" last_modified = Gtn::ModificationTimes.obtain_time(page.path) notebook = GTNNotebooks.render_jupyter_notebook(page.data, page.content, page.url, last_modified, notebook_language, site, dir) topic_id = dir.split('/')[-3] tutorial_id = dir.split('/')[-1] with_solutions = notebook.clone with_solutions['cells'] = with_solutions['cells'].map do |cell| if cell.fetch('cell_type') == 'markdown' && (cell['source'].is_a? String) m = cell['source'].match(/<blockquote class="solution"[^>]*>/) if m cell['source'].gsub!(/<blockquote class="solution"[^>]*>/, '<br/><details style="border: 2px solid #B8C3EA; margin: 1em 0.2em;' \ 'padding: 0.5em; cursor: pointer;"><summary>👁 View solution</summary>') idx = m.begin(0) q = cell['source'][0..idx] w = cell['source'][idx + 1..] e = w.index('</blockquote>') r = "#{w[0..e - 1]}</details>#{w[e + 13..]}" cell['source'] = q + r end end cell end # Write it out! ipynb_dir = File.join(site.dest, dir) ipynb_path = File.join(ipynb_dir, "#{topic_id}-#{tutorial_id}.ipynb") # page2 = PageWithoutAFile.new(site, '', dir, "#{topic_id}-#{tutorial_id}.ipynb") # page2.content = JSON.pretty_generate(with_solutions) # page2.data['layout'] = nil # page2.data['citation_target'] = 'jupyter' # site.pages << page2 # Create a no-solutions version: no_solutions = notebook.clone no_solutions['cells'] = no_solutions['cells'].map do |cell| if cell.fetch('cell_type') == 'markdown' && (cell['source'].is_a? String) cell['source'].gsub!(/<blockquote class="solution"[^>]*>/, '<blockquote class="solution" style="display:none">') end cell end ipynb_path2 = File.join(ipynb_dir, "#{topic_id}-#{tutorial_id}-course.ipynb") # page2 = PageWithoutAFile.new(site, '', dir, "#{topic_id}-#{tutorial_id}-course.ipynb") # page2.content = JSON.pretty_generate(no_solutions) # page2.data['layout'] = nil # page2.data['citation_target'] = 'jupyter' # site.pages << page2 site.config['__rendered_notebook_cache'][page.path] = { 'dir' => ipynb_dir, 'path1' => ipynb_path, 'content1' => JSON.pretty_generate(json_boxify(with_solutions, page)), 'path2' => ipynb_path2, 'content2' => JSON.pretty_generate(json_boxify(no_solutions, page)), } end end
linkify(text, path)
click to toggle source
# File bin/news.rb, line 83 def linkify(text, path) "[#{text.gsub('|', '-')}](https://training.galaxyproject.org/training-material/#{path}?utm_source=matrix&utm_medium=newsbot&utm_campaign=matrix-news)" end
lookup_topic(topic_id)
click to toggle source
# File bin/prepare_feedback.rb, line 54 def lookup_topic(topic_id) @cache ||= {} @cache.fetch(topic_id) do |key| file = "metadata/#{topic_id}.yaml" return nil unless File.exist? file data = YAML.load_file(file) @cache[key] = data['title'] end end
lookup_tuto(topic_id, tuto_id)
click to toggle source
# File bin/prepare_feedback.rb, line 33 def lookup_tuto(topic_id, tuto_id) @cache ||= {} @cache.fetch("#{topic_id}/#{tuto_id}") do |key| @cache[key] = nil file = "topics/#{topic_id}/tutorials/#{tuto_id}/tutorial.md" if File.exist? file data = YAML.load_file(file) @cache[key] = data['title'] else file = "topics/#{topic_id}/tutorials/#{tuto_id}/slides.html" if File.exist? file data = YAML.load_file(file) @cache[key] = data['title'] else puts "No file for #{topic_id}/#{tuto_id}" end end end end
onlyEnabled(x)
click to toggle source
# File bin/news.rb, line 72 def onlyEnabled(x) tutorial_meta = YAML.load_file(x) tutorial_enabled = tutorial_meta.fetch('enable', true) topic = x.split('/')[1] topic_meta = YAML.load_file("metadata/#{topic}.yaml") topic_enabled = topic_meta.fetch('enable', true) tutorial_enabled and topic_enabled end
parseOptions()
click to toggle source
# File bin/ari-synthesize.rb, line 157 def parseOptions options = {} OptionParser.new do |opts| opts.banner = 'Usage: ari-synthesize.rb [options]' options[:neural] = true options[:voice] = 'Amy' options[:lang] = 'en-GB' opts.on('--aws', 'Use AWS Polly') do |v| options[:aws] = v end opts.on('--mozilla', 'Use MozillaTTS') do |v| options[:mozilla] = v end opts.on('--non-neural', '[AWS] Non-neural voice') do |_v| options[:neural] = false end opts.on('--voice=VOICE', '[AWS] Voice ID') do |n| options[:voice] = n end opts.on('--lang=LANG', '[AWS] Language code') do |n| options[:lang] = n end opts.on('-fFILE', '--file=FILE', 'File containing line of text to speak') do |n| options[:file] = n end opts.on('-oFILE', '--output=FILE', 'Location to save the file in (defaults to auto-generated location)') do |n| options[:output] = n end opts.on('-v', '--[no-]verbose', 'Run verbosely') do |v| options[:verbose] = v end end.parse! if !(options[:aws] || options[:mozilla]) puts 'ERROR: You must use aws or mozilla' exit 1 end if !(options[:file]) puts 'ERROR: You must provide a file with a single sentence to speak' exit 1 end sentence = File.read(options[:file]).chomp if options[:aws] engine = 'aws' elsif options[:mozilla] engine = 'mozilla' end [sentence, engine, options] end
parse_metadata(path)
click to toggle source
# File bin/update-data-library, line 149 def parse_metadata(path) parts = path.to_s.split('/') topic_id = parts[1] topic_metadata = YAML.load_file(File.join('metadata', "#{topic_id}.yaml")) tutorial_metadata = YAML.load_file(path) topic = { 'name' => topic_metadata['title'], 'description' => topic_metadata['summary'] } tutorial = { 'name' => tutorial_metadata['title'] } [topic, tutorial] end
parse_tutorial_for_zenodo_link(path)
click to toggle source
# File bin/update-data-library, line 143 def parse_tutorial_for_zenodo_link(path) parse_zenodo_id_formats(YAML.load_file(path)['zenodo_link']) rescue StandardError nil end
parse_zenodo_id_formats(link)
click to toggle source
# File bin/update-data-library, line 30 def parse_zenodo_id_formats(link) # https://zenodo.org/record/1234567 # https://zenodo.org/record/1234567#.X0X0X0X0X0X # doi:10.5281/zenodo.1234567 # doi:10.5281/zenodo.1234567#.X0X0X0X0X0X # 10.5281/zenodo.1234567 # 10.5281/zenodo.1234567#.X0X0X0X0X0X # https://doi.org/10.5281/zenodo.3732358 # https://doi.org/10.5281/zenodo.3732358#.X0X0X0X0X0X # link = link.split('#')[0] if link.match(/doi:/) || link.match(/^10.5281/) || link.match(/doi.org/) link.split('.')[-1] else link.split('/')[-1] end end
printableMaterial(path)
click to toggle source
# File bin/news.rb, line 87 def printableMaterial(path) d = YAML.load_file(path) { md: linkify(d['title'], path.gsub(/.md/, '.html')), path: path } end
request(url)
click to toggle source
# File bin/update-data-library, line 17 def request(url) uri = URI.parse(url) request = Net::HTTP::Get.new(uri) request['Accept'] = 'application/json' req_options = { use_ssl: uri.scheme == 'https', } Net::HTTP.start(uri.hostname, uri.port, req_options) do |http| json_s = http.request(request).body JSON.parse(json_s) end end
send_news(output, options, channel: 'default')
click to toggle source
# File bin/news.rb, line 213 def send_news(output, options, channel: 'default') if options[:postToMatrix] # rubocop:disable Style/GlobalVars homeserver = $rooms[channel] # rubocop:enable Style/GlobalVars pp homeserver data = { 'msgtype' => 'm.notice', 'body' => output, 'format' => 'org.matrix.custom.html', 'formatted_body' => Kramdown::Document.new(output).to_html, } headers = { 'Authorization' => "Bearer #{ENV.fetch('MATRIX_ACCESS_TOKEN', nil)}", 'Content-type' => 'application/json', } uri_send_message = URI("#{homeserver[:server]}/_matrix/client/r0/rooms/#{homeserver[:room]}/send/m.room.message") req = Net::HTTP.post(uri_send_message, JSON.generate(data), headers) # Parse response resp = JSON.parse(req.body) puts resp if resp['errcode'] == 'M_FORBIDDEN' && (resp['error'] =~ /not in room/) puts 'Not in room, attempting to join' # Join room # POST /_matrix/client/v3/join/{roomIdOrAlias} uri_join = URI("#{homeserver[:server]}/_matrix/client/v3/join/#{homeserver[:room]}") req = Net::HTTP.post(uri_join, JSON.generate({}), headers) # Parse response resp = JSON.parse(req.body) # Now we're safe to re-try if resp.key?('room_id') req = Net::HTTP.post(uri_send_message, JSON.generate(data), headers) # Parse response resp = JSON.parse(req.body) puts resp end end else puts '==============' puts output puts '==============' end end
show_errors(file, errs)
click to toggle source
# File bin/validate-contributors.rb, line 29 def show_errors(file, errs) # If we had no errors, validated successfully if errs.empty? puts "\e[38;5;40m#{file} validated succesfully\e[m" 0 else # Otherwise, print errors and exit non-zero puts "\e[48;5;09m#{file} has errors\e[m" errs.each { |x| puts " #{x}" } 1 end end
split_sentence(sentence, timing)
click to toggle source
# File bin/ari-prep-script.rb, line 56 def split_sentence(sentence, timing) res = sentence.split chunk_size = (res.length.to_f / (res.length.to_f / 20).ceil).ceil chunks = res.each_slice(chunk_size).to_a.length res.each_slice(chunk_size).with_index.map do |chunk, idx| t0 = timing * (idx / chunks.to_f) tf = timing * ((1 + idx) / chunks.to_f) [chunk, t0, tf] end end
synthesize(uncorrected_line, engine, voice: 'Amy', lang: 'en-GB', neural: true, output: nil)
click to toggle source
# File bin/ari-synthesize.rb, line 101 def synthesize(uncorrected_line, engine, voice: 'Amy', lang: 'en-GB', neural: true, output: nil) line = correct(uncorrected_line) digest = Digest::MD5.hexdigest line if output.nil? mp3 = File.join(GTN_CACHE, "#{engine}-#{digest}-#{voice}.mp3") json = File.join(GTN_CACHE, "#{engine}-#{digest}-#{voice}.json") if File.file?(mp3) duration = JSON.parse(File.read(json))['end'] return mp3, json, duration.to_f end else mp3 = output json = "#{output}.json" if File.file?(output) return mp3, json, 0.0 # Todo end end # Call our engine call_engine(engine, line, mp3, voice, lang, neural) duration = find_duration(mp3) if line.length < 200 && duration > 27 # Helena managed to find a specific bad string which, when fed to Mozilla's # TTS would generate # # In: Some important terms you should know. # Out Some important terms you should know know know know know know know know know know know know know know ... # # So we put in a check that the duration hasn't done something crazy, and # if it is add something to the end which seems to short-circuit that # error. # # I've reported this upstream but the response was not useful, apparently # this is an "expected failure mode". # # https://github.com/synesthesiam/docker-mozillatts/issues/9 # https://discourse.mozilla.org/t/sentences-which-trigger-an-endless-loop/72261/8 warn 'Strange: line was too long' call_engine(engine, "#{line}.", mp3) duration = find_duration(mp3) end if line.length < 200 && duration > 27 # Or maybe they just wrote a super long sentence. Or maybe we need to update the cutoff time. warn "ERROR: #{duration} of line is bad: #{line}" end # Now collect metadata for JSON json_handle = File.open(json, 'w') json_handle.write(JSON.generate({ time: 0, type: 'sentence', start: 0, end: duration, value: line })) json_handle.close [mp3, json, duration] end
test_workflow(workflow_file, galaxy_id)
click to toggle source
# File bin/workflow-test.rb, line 16 def test_workflow(workflow_file, galaxy_id) directory = File.dirname(workflow_file) workflow_base = File.basename(workflow_file, '.ga') workflow_output_json = File.join(directory, "#{workflow_base}.#{galaxy_id}.json") galaxy_url = GALAXIES[galaxy_id][:url] galaxy_user_key = GALAXIES[galaxy_id][:key] cmd = [ 'planemo', '--verbose', 'test', '--galaxy_url', galaxy_url, '--galaxy_user_key', galaxy_user_key, '--no_shed_install', '--engine', 'external_galaxy', '--polling_backoff', '1', '--simultaneous_uploads', '--test_output_json', workflow_output_json, workflow_file ] p cmd.join(' ') Open3.popen3(*cmd) do |_stdin, stdout, stderr, wait_thr| exit_status = wait_thr.value # Process::Status object returned File.write("#{directory}/#{workflow_base}.#{galaxy_id}.log", stdout.read) File.write("#{directory}/#{workflow_base}.#{galaxy_id}.err", stderr.read) puts "#{workflow_file} => #{exit_status} (#{stderr})" end end
timefmt(t, fmt)
click to toggle source
Intro slides. Fast: editly –json editly.json5 126,23s user 5,62s system 126% cpu 1:44,08 total Slow: editly –json editly.json5 902,71s user 69,27s system 326% cpu 4:57,54 total
# File bin/ari-prep-script.rb, line 39 def timefmt(t, fmt) seconds = t % (24 * 3600) hours = seconds.to_i / 3600 seconds = seconds % 3600 minutes = seconds.to_i / 60 seconds = seconds % 60 (seconds, ms) = seconds.divmod(1) # seconds = seconds ms = 1000 * ms if fmt == 'vtt' format('%<h>02d:%<m>02d:%<s>02d.%<ms>03d', { h: hours, m: minutes, s: seconds, ms: ms }) else format('%<h>02d:%<m>02d:%<s>02d,%<ms>03d', { h: hours, m: minutes, s: seconds, ms: ms }) end end
titleize(t)
click to toggle source
# File bin/news.rb, line 128 def titleize(t) t.gsub('-', ' ').gsub(/\w+/, &:capitalize) end
translate(word)
click to toggle source
# File bin/ari-synthesize.rb, line 24 def translate(word) return word if /^\s+$/.match(word) return word if PUNCTUATION.find_index(word) return WORD_MAP[word] if WORD_MAP.key?(word) m = /([^A-Za-z0-9]*)([A-Za-z0-9]+)([^A-Za-z0-9]*)(.*)/.match(word) if !m puts "Error: #{word}" return word end fixed = if m[2] WORD_MAP.fetch(m[2].downcase, m[2]) else m[2] end # puts "#{m} ⇒ #{m[1] + fixed + m[3]}" m[1] + fixed + m[3] + m[4] end
update_data_library(path, topic, tutorial, zenodo_record)
click to toggle source
# File bin/update-data-library, line 55 def update_data_library(path, topic, tutorial, zenodo_record) zenodo_id = zenodo_record['id'].to_s zenodo_files = zenodo_record.fetch('files', []).map do |f| official_extension = f['type'] link = f['links']['self'].sub(%r{/content$}, '') unofficial_extension = link.split('.')[-2..].join('.') ext = @SHARED_DATATYPES.fetch(unofficial_extension, nil) || @SHARED_DATATYPES.fetch(official_extension, nil) # Example: # https://zenodo.org/api/records/10870107/files/elem_s2_r1.fq.gz/content # Needs to be # https://zenodo.org/record/10870107/files/elem_s2_r1.fq.gz real_link = f['links']['self'].sub(%r{/content$}, '').sub('/api/records/', '/record/') # puts "Processing file: #{f['type']} #{f['links']['self']} => #{ext}" # puts "#{unofficial_extension} => #{@SHARED_DATATYPES.fetch(unofficial_extension, nil)}" # puts "#{official_extension} => #{@SHARED_DATATYPES.fetch(official_extension, nil)}" warn "Unknown file type: #{f['type']}. Consider adding this to shared/datatypes.yaml" if ext.nil? { 'url' => real_link, 'src' => 'url', 'ext' => ext || f['type'], 'info' => "https://doi.org/10.5281/zenodo.#{zenodo_id}", # 'checksum' => f['checksum'], # 'key' => f['key'], } end library = { 'destination' => { 'type' => 'library', 'name' => 'GTN - Material', 'description' => 'Galaxy Training Network Material', 'synopsis' => 'Galaxy Training Network Material. See https://training.galaxyproject.org', }, 'items' => [ 'name' => topic['name'], 'description' => topic['description'], 'items' => [ 'name' => tutorial['name'], 'items' => [ 'name' => "DOI: 10.5281/zenodo.#{zenodo_id}", 'description' => 'latest', 'items' => zenodo_files ] ] ] } data_library_path = data_library_for_tutorial(path) puts "Writing data library to #{data_library_path}" File.write(data_library_path, library.to_yaml) end
update_tutorial(path, zenodo_id)
click to toggle source
# File bin/update-data-library, line 48 def update_tutorial(path, zenodo_id) # Edit the yaml header of the markdown file to update the ID contents = File.read(path) contents.gsub!(/^zenodo_link:.*/, "zenodo_link: 'https://zenodo.org/record/#{zenodo_id}'") File.write(path, contents) end
validate_document(document, validator)
click to toggle source
# File bin/validate-contributors.rb, line 22 def validate_document(document, validator) errors = validator.validate(document) return errors if errors && !errors.empty? [] end
write_data_library(path, topic, tutorial, tutorial_zenodo_id, force)
click to toggle source
# File bin/update-data-library, line 109 def write_data_library(path, topic, tutorial, tutorial_zenodo_id, force) # Fetch the zenodo record zenodo_record = request("https://zenodo.org/api/records/#{tutorial_zenodo_id}") new_zenodo_id = zenodo_record['id'].to_s # If it's redirected we'll get a different ID here puts "Discovered zenodo link: #{new_zenodo_id}" # So load the data library, and check what's written there datalibrary_zenodo_id = if File.exist?(data_library_for_tutorial(path)) YAML.load_file(data_library_for_tutorial(path))['zenodo_id'].to_s end # If the ID has changed we should update the tutorial as well: if new_zenodo_id == tutorial_zenodo_id && !force warn 'Tutorial is up to date' else warn "Zenodo ID has changed from #{tutorial_zenodo_id} to #{new_zenodo_id}, updating the tutorial" update_tutorial(path, new_zenodo_id) end # If the new ID doesn't match the data library, then we should update it. if new_zenodo_id == datalibrary_zenodo_id && !force warn 'Data library is up to date' else warn "Zenodo ID has changed from #{datalibrary_zenodo_id} to #{new_zenodo_id}, updating the data library" update_data_library(path, topic, tutorial, zenodo_record) end end