module Jekyll::GtnFunctions
The main GTN function library
Constants
- ELIXIR_NODES
List of elixir node country IDs (ISO 3166-1 alpha-2) and their names
Public Class Methods
rubocop:disable Naming/PredicateName
# File _plugins/gtn.rb, line 34 def self.cache @@cache ||= Jekyll::Cache.new('GtnFunctions') end
Public Instance Methods
# File _plugins/gtn.rb, line 654 def collapse_date_pretty(event) collapse_event_date_pretty(event) end
# File _plugins/gtn.rb, line 345 def convert_to_material_list(site, materials) # [{"name"=>"introduction", "topic"=>"admin"}] return [] if materials.nil? materials.map do |m| if m.key?('name') && m.key?('topic') found = TopicFilter.fetch_tutorial_material(site, m['topic'], m['name']) Jekyll.logger.warn "Could not find material #{m['topic']}/#{m['name']} in the site data" if found.nil? if m.key?('time') found['time'] = m['time'] end found elsif m.key?('external') && m['external'] { 'layout' => 'tutorial_hands_on', 'name' => m['name'], 'title' => m['name'], 'hands_on' => 'external', 'hands_on_url' => m['link'], } elsif m.key?('type') && m['type'] == 'custom' { 'layout' => 'custom', 'name' => m['name'], 'title' => m['name'], 'description' => m['description'], 'time' => m['time'], } else Jekyll.logger.warn "[GTN] Unsure how to render #{m}" end end end
Convert a workflow path to a TRS path Params:
str
-
The workflow path
Returns:
String
-
The TRS path
Example:
{{ "topics/metagenomics/tutorials/mothur-miseq-sop-short/workflows/workflow1_quality_control.ga" | convert_workflow_path_to_trs }} => "/api/ga4gh/trs/v2/tools/metagenomics-mothur-miseq-sop-short/versions/workflow1_quality_control"
# File _plugins/gtn.rb, line 391 def convert_workflow_path_to_trs(str) return 'GTN_TRS_ERROR_NIL' if str.nil? m = str.match(%r{topics/(?<topic>.*)/tutorials/(?<tutorial>.*)/workflows/(?<workflow>.*)\.ga}) return "/api/ga4gh/trs/v2/tools/#{m[:topic]}-#{m[:tutorial]}/versions/#{m[:workflow].downcase}" if m 'GTN_TRS_ERROR' end
Returns the name of an elixir node, given its country ID Params:
name
-
The country ID of the node (ISO 3166-1 alpha-2)
Returns:
String
-
The name of the node
# File _plugins/gtn.rb, line 71 def elixirnode2name(name) ELIXIR_NODES[name] end
Convert a fedi address to a link Params:
fedi_address
-
The fedi address to convert
Returns:
String
-
The URL at which their profile is accessible
Example:
{{ contributors[page.contributor].fediverse | fedi2link }} fedi2link("@hexylena@galaxians.garden") => "https://galaxians.garden/@hexylena"
# File _plugins/gtn.rb, line 312 def fedi2link(fedi_address) fedi_address.gsub(/^@?(?<user>.*)@(?<host>.*)$/) { |_m| "https://#{$LAST_MATCH_INFO[:host]}/@#{$LAST_MATCH_INFO[:user]}" } end
Params:
data
-
The site data
string
-
The contributor id
Returns:
Hash
-
The contributing entity
Example:
{% assign contrib = site | fetch_contributor: page.contributor -%}
# File _plugins/gtn.rb, line 245 def fetch_contributor(site, id) Gtn::Contributors.fetch_contributor(site, id) end
Params:
data
-
The contributor’s data
Returns:
String
-
The funding URL
Example:
{{ entity | fetch_entity_avatar: 'alice', 120 }}
# File _plugins/gtn.rb, line 291 def fetch_entity_avatar(entity, id, width) if entity.nil? return '<img src="/training-material/assets/images/avatar.png" alt="ERROR_NO_ENTITY avatar" class="avatar"/>' end w = width.nil? ? '' : "width=\"#{width}\"" url = fetch_entity_avatar_url(entity, id, width) %(<img src="#{url}" alt="#{entity['name']} avatar" #{w} class="avatar" />) end
Params:
data
-
The contributor’s data
Returns:
String
-
The avatar’s URL
Example:
{{ entity | fetch_entity_avatar: 'alice', 120 }}
# File _plugins/gtn.rb, line 269 def fetch_entity_avatar_url(entity, id, width) return 'ERROR_NO_ENTITY' if entity.nil? width.nil? ? '' : "width=\"#{width}\"" if !entity['avatar'].nil? entity['avatar'] elsif entity['github'] != false qp = width.nil? ? '' : "?s=#{width}" "https://avatars.githubusercontent.com/#{id}#{qp}" else '/training-material/assets/images/avatar.png' end end
Params:
data
-
The contributor’s data
Returns:
String
-
The funding URL
Example:
{{ entity | fetch_funding_url }}
# File _plugins/gtn.rb, line 257 def fetch_funding_url(entity) Gtn::Contributors.fetch_funding_url(entity) end
# File _plugins/gtn.rb, line 832 def find_learningpaths_including_topic(site, topic_id) site.pages .select { |p| p['layout'] == 'learning-pathway' } .select do |p| materials_for_pathway(p) .map { |topic, _tutorial| topic } .include?(topic_id) end end
Fix the titles of boxes in a page Params:
content
-
The content to fix
lang
-
The language of the content
key
-
The key of the content
Returns:
String
-
The fixed content
# File _plugins/gtn.rb, line 220 def fix_box_titles(content, lang, key) Gtn::Boxify.replace_elements(content, lang, key) end
# File _plugins/gtn.rb, line 583 def format_location(location) url = 'https://www.openstreetmap.org/search?query=' # location: # name: Bioinf Dept # address: 42 E Main St. # city: Reyjkjavik # country: Iceland # #region: # optional # postcode: 912NM loc = [ location.fetch('name', nil), location.fetch('address', nil), location.fetch('city', nil), location.fetch('region', nil), location.fetch('country', nil), location.fetch('postcode', nil) ].compact if loc.length > 1 "<a href=\"#{url}#{loc.join(', ')}\">#{loc.join(', ')}</a>" else # Just e.g. the name loc.join(', ') end end
# File _plugins/gtn.rb, line 622 def format_location_short(location) url = 'https://www.openstreetmap.org/search?query=' # location: # name: Bioinf Dept # address: 42 E Main St. # city: Reyjkjavik # country: Iceland # #region: # optional # postcode: 912NM loc = [ location.fetch('name', nil), location.fetch('address', nil), location.fetch('city', nil), location.fetch('region', nil), location.fetch('country', nil), location.fetch('postcode', nil) ].compact loc2 = [ location.fetch('name', nil), location.fetch('city', nil), location.fetch('country', nil) ].compact if loc.length > 1 "<a href=\"#{url}#{loc.join(', ')}\">#{loc2.join(', ')}</a>" else # Just e.g. the name loc.join(', ') end end
# File _plugins/gtn.rb, line 609 def format_location_simple(location) loc = [ location.fetch('name', nil), location.fetch('address', nil), location.fetch('city', nil), location.fetch('region', nil), location.fetch('country', nil), location.fetch('postcode', nil) ].compact loc.join(', ') end
Gets the ‘default’ link for a material, hands on if it exists, otherwise slides. Params:
material
-
The material to get the link for
Returns:
String
-
The URL of the default link
# File _plugins/gtn.rb, line 799 def get_default_link(material) return 'NO LINK' if material.nil? return 'NO LINK' if material == true url = nil url = "topics/#{material['topic_name']}/tutorials/#{material['tutorial_name']}/slides.html" if material['slides'] if material['hands_on'] && (material['hands_on'] != 'external' && material['hands_on'] != '') url = "topics/#{material['topic_name']}/tutorials/#{material['tutorial_name']}/tutorial.html" end url end
# File _plugins/gtn.rb, line 497 def get_feedback_count(site, material_id) get_feedbacks(site, material_id).length end
# File _plugins/gtn.rb, line 501 def get_feedback_count_recent(site, material_id) get_recent_feedbacks_time(site, material_id).length end
# File _plugins/gtn.rb, line 465 def get_feedbacks(site, material_id) return [] if material_id.nil? begin topic, tutorial = material_id.split('/') if tutorial.include?(':') language = tutorial.split(':')[1] tutorial = tutorial.split(':')[0] # If a language is supplied, then feedbacks = site.data['feedback2'][topic][tutorial] .select { |f| (f['lang'] || '').downcase == language.downcase } else # English is the default feedbacks = site.data['feedback2'][topic][tutorial] .select { |f| f['lang'].nil? } end rescue StandardError return [] end return [] if feedbacks.nil? || feedbacks.empty? feedbacks .sort_by { |f| f['date'] } .reverse .map do |f| f['stars'] = to_stars(f['rating']) f end end
# File _plugins/gtn.rb, line 738 def get_og_desc(site, page); end
# File _plugins/gtn.rb, line 740 def get_og_title(site, page, reverse) og_title = [] topic_id = page['path'].gsub(%r{^\./}, '').split('/')[1] if site.data.key?(topic_id) if site.data[topic_id].is_a?(Hash) && site.data[topic_id].key?('title') og_title = [site.data[topic_id]['title'].clone] else Jekyll.logger.warn "Missing title for #{topic_id}" end end if page['layout'] == 'topic' og_title.push 'Tutorial List' return og_title.join(' / ') end material_id = page['path'].gsub(%r{^\./}, '').split('/')[3] material = nil material = fetch_tutorial_material(site, topic_id, material_id) if site.data.key? topic_id og_title.push material['title'] if !material.nil? case page['layout'] when 'workflow-list' og_title.push 'Workflows' when 'faq-page', 'faqs' if page['path'] =~ %r{faqs/gtn} og_title.push 'GTN FAQs' elsif page['path'] =~ %r{faqs/galaxy} og_title.push 'Galaxy FAQs' else og_title.push 'FAQs' end when 'faq' og_title.push "FAQ: #{page['title']}" when 'learning-pathway' og_title.push "Learning Pathway: #{page['title']}" when 'tutorial_hands_on' og_title.push "Hands-on: #{page['title']}" when /slides/ og_title.push "Slides: #{page['title']}" else og_title.push page['title'] end if reverse.to_s == 'true' og_title.compact.reverse.join(' / ').gsub(/Hands-on: Hands-on:/, 'Hands-on:') else og_title.compact.join(' / ').gsub(/Hands-on: Hands-on:/, 'Hands-on:') end end
# File _plugins/gtn.rb, line 441 def get_rating(site, material_id, recent: false) f = get_rating_histogram(site, material_id, recent: recent) rating = f.map { |k, v| k * v }.sum / f.map { |_k, v| v }.sum.to_f rating.round(1) end
# File _plugins/gtn.rb, line 419 def get_rating_histogram(site, material_id, recent: false) return {} if material_id.nil? feedbacks = recent ? get_recent_feedbacks_time(site, material_id) : get_feedbacks(site, material_id) return {} if feedbacks.nil? || feedbacks.empty? ratings = feedbacks.map { |f| f['rating'] } ratings.each_with_object(Hash.new(0)) { |w, counts| counts[w] += 1 } end
# File _plugins/gtn.rb, line 430 def get_rating_histogram_chart(site, material_id) histogram = get_rating_histogram(site, material_id) return {} if histogram.empty? highest = histogram.map { |_k, v| v }.max histogram .map { |k, v| [k, [v, v / highest.to_f]] } .sort_by { |k, _v| -k } .to_h end
# File _plugins/gtn.rb, line 447 def get_rating_recent(site, material_id) r = get_rating(site, material_id, recent: true) r.nan? ? get_rating(site, material_id, recent: false) : r end
# File _plugins/gtn.rb, line 519 def get_recent_feedbacks(site, material_id) feedbacks = get_feedbacks(site, material_id) .select do |f| f['pro']&.length&.positive? || f['con']&.length&.positive? end .map do |f| f['f_date'] = Date.parse(f['date']).strftime('%B %Y') f end last_year = feedbacks.select { |f| Date.parse(f['date']) > Date.today - 365 } # If we have fewer than 20 in the last year, then extend further. if last_year.length < 20 feedbacks .first(20) .group_by { |f| f['f_date'] } else # Otherwise just everything last year. last_year .group_by { |f| f['f_date'] } end end
# File _plugins/gtn.rb, line 505 def get_recent_feedbacks_time(site, material_id) feedbacks = get_feedbacks(site, material_id) .select do |f| f['pro']&.length&.positive? || f['con']&.length&.positive? end .map do |f| f['f_date'] = Date.parse(f['date']).strftime('%B %Y') f end feedbacks.select { |f| Date.parse(f['date']) > Date.today - 365 } end
Get the topic of a page’s path Params:
page
-
The page to get the topic of, it will inspect page
Returns:
String
-
The topic of the page
Example:
{{ page | get_topic }}
# File _plugins/gtn.rb, line 667 def get_topic(page) # Arrays that will store all introduction slides and tutorials we discover. page['path'].split('/')[1] end
Get the list of ‘upcoming’ events (i.e. reg deadline or start is 30 days away.) Params:
site
-
The site object
Returns:
Array
-
List of events
Example:
{{ site | get_upcoming_events }}
# File _plugins/gtn.rb, line 681 def get_upcoming_events(site) cache.getset('upcoming-events') do site.pages .select { |p| p.data['layout'] == 'event' || p.data['layout'] == 'event-external' } .reject { |p| p.data['program'].nil? } # Only those with programs .select { |p| p.data['event_upcoming'] == true } # Only those coming soon .map do |p| materials = p.data['program'] .map { |section| section['tutorials'] } .flatten .compact # Remove nil entries .reject { |x| x.fetch('type', nil) == 'custom' } # Remove custom entries .map { |x| "#{x['topic']}/#{x['name']}" } # Just the material IDs. .sort.uniq [p, materials] end end end
Get the list of ‘upcoming’ events that include this material’s ID Params:
site
-
The site object
material
-
The ‘material’ to get the topic of, it will inspect page.id (use new_material)
Returns:
Array
-
List of events
Example:
{{ site | get_upcoming_events }}
# File _plugins/gtn.rb, line 710 def get_upcoming_events_for_this(site, material) if material.nil? [] else get_upcoming_events(site) .select { |_p, materials| materials.include? material['id'] } .map { |p, _materials| p } end end
# File _plugins/gtn.rb, line 415 def get_version_number(page) Gtn::ModificationTimes.obtain_modification_count(page['path']) end
# File _plugins/gtn.rb, line 814 def group_icons(icons) icons.group_by { |_k, v| v }.transform_values { |v| v.map { |z| z[0] } }.invert end
Returns the last modified date of a page Params:
page
-
The page to get the last modified date of
Returns:
String
-
The last modified date of the page
# File _plugins/gtn.rb, line 177 def gtn_mod_date(path) # Automatically strips any leading slashes. Gtn::ModificationTimes.obtain_time(path.gsub(%r{^/}, '')) end
Returns the publication date of a page, when it was merged into ‘main` Params:
page
-
The page to get the publication date of
Returns:
String
-
The publication date of the page
# File _plugins/gtn.rb, line 151 def gtn_pub_date(path) # if it's not a string then log a warning path = path['path'] if !path.is_a?(String) # Automatically strips any leading slashes. Gtn::PublicationTimes.obtain_time(path.gsub(%r{^/}, '')) end
How many times has a topic been mentioned in feedback? Params:
feedback
-
The feedback to search through
name
-
The name of the topic to search for
Returns:
Integer
-
The number of times the topic has been mentioned
# File _plugins/gtn.rb, line 189 def how_many_topic_feedbacks(feedback, name) if feedback.nil? return 0 end feedback.select { |x| x['topic'] == name }.length end
How many times has a tutorial been mentioned in feedback? Params:
feedback
-
The feedback to search through
name
-
The name of the tutorial to search for
Returns:
Integer
-
The number of times the tutorial has been mentioned
# File _plugins/gtn.rb, line 204 def how_many_tutorial_feedbacks(feedback, name) if feedback.nil? return 0 end feedback.select { |x| x['tutorial'] == name }.length end
Return human text for ruby types Params:
type
-
The type to humanize
Returns:
String
-
The humanized type
Example:
humanize_types("seq") # => "List of Items"
# File _plugins/gtn.rb, line 126 def humanize_types(type) data = { 'seq' => 'List of Items', 'str' => 'Free Text', 'map' => 'A dictionary/map', 'float' => 'Decimal Number', 'int' => 'Integer Number', 'bool' => 'Boolean' } data[type] end
# File _plugins/gtn.rb, line 728 def is_date_passed(date) if date.nil? false elsif date.is_a?(String) Date.parse(date) < Date.today else date < Date.today end end
Returns the last modified date of a page Params:
page
-
The page to get the last modified date of
Returns:
String
-
The last modified date of the page
TODO: These two could be unified tbh
# File _plugins/gtn.rb, line 166 def last_modified_at(page) Gtn::ModificationTimes.obtain_time(page['path']) end
# File _plugins/gtn.rb, line 400 def layout_to_human(layout) case layout when /_slides/ # excludes slides-plain 'Slides' when /tutorial_hands_on/ 'Hands-on' when 'faq' 'FAQs' when 'news' 'News' when 'workflow' 'Workflow' end end
# File _plugins/gtn.rb, line 563 def list_usegalaxy_servers(_site) Gtn::Usegalaxy.servers.map { |x| x.transform_keys(&:to_s) } end
# File _plugins/gtn.rb, line 567 def list_usegalaxy_servers_shuffle(_site) Gtn::Usegalaxy.servers.map { |x| x.transform_keys(&:to_s) }.shuffle end
Load an SVG file directly into the page Params:
path
-
The path of the SVG file (relative to GTN workspace root)
Returns:
String
-
The SVG file contents
Example:
{{ "assets/images/mastodon.svg" | load_svg }}
# File _plugins/gtn.rb, line 325 def load_svg(path) File.read(path).gsub(/\R+/, '') end
# File _plugins/gtn.rb, line 818 def materials_for_pathway(page) d = if page.is_a?(Jekyll::Page) page.data.fetch('pathway', []) else page.fetch('pathway', []) end d.map do |m| m.fetch('tutorials', []) .select { |t| t.key?('name') && t.key?('topic') } .map { |t| [t['topic'], t['name']] } end.flatten.compact.sort.uniq end
# File _plugins/gtn.rb, line 329 def regex_replace(str, regex_search, value_replace) regex = /#{regex_search}/m str.gsub(regex, value_replace) end
Replaces newlines with newline + two spaces
# File _plugins/gtn.rb, line 140 def replace_newline_doublespace(text) text.gsub(/\n/, "\n ") end
# File _plugins/gtn.rb, line 720 def shuffle(array) array.shuffle end
A slightly more unsafe slugify function Params:
text
-
The text to slugify
Returns:
String
-
The slugified text
Example:
slugify_unsafe("Hello, World!") # => "Hello-World"
# File _plugins/gtn.rb, line 112 def slugify_unsafe(text) # Gets rid of *most* things without making it completely unusable? unsafe_slugify(text) end
Only accepts an integer rating
# File _plugins/gtn.rb, line 453 def to_stars(rating) if rating.nil? || (rating.to_i < 1) || (rating == '0') || rating.zero? %(<span class="sr-only">0 stars</span>) + '<i class="far fa-star" aria-hidden="true"></i>' elsif rating.to_i < 1 '<span class="sr-only">0 stars</span><i class="far fa-star" aria-hidden="true"></i>' else %(<span class="sr-only">#{rating} stars</span>) + ('<i class="fa fa-star" aria-hidden="true"></i>' * rating.to_i) end end
Obtain the most cited paper in the GTN Params:
citations
-
The citations to search through
Returns:
Hash
-
The papers including their text citation and citation count
# File _plugins/gtn.rb, line 93 def top_citations(citations) if citations.nil? {} else citations.sort_by { |_k, v| v }.reverse.to_h.first(20).to_h do |k, v| [k, { 'count' => v, 'text' => Gtn::Scholar.render_citation(k) }] end end end
# File _plugins/gtn.rb, line 571 def topic_name_from_page(page, site) if page.key? 'topic_name' site.data[page['topic_name']]['title'] elsif page['url'] =~ /^\/faqs\/gtn/ 'GTN FAQ' elsif page['url'] =~ /^\/faqs\/galaxy/ 'Galaxy FAQ' else site.data.fetch(page['url'].split('/')[2], { 'title' => '' })['title'] end end
# File _plugins/gtn.rb, line 543 def tutorials_over_time_bar_chart(site) graph = Hash.new(0) TopicFilter.list_all_materials(site).each do |material| if material['pub_date'] yymm = material['pub_date'].strftime('%Y-%m') graph[yymm] += 1 end end # Cumulative over time # https://stackoverflow.com/questions/71745593/how-to-do-a-single-line-cumulative-count-for-hash-values-in-ruby graph # Turns it into an array .sort_by { |k, _v| k } # Cumulative sum .each_with_object([]) { |(k, v), a| a << [k, v + a.last&.last.to_i] }.to_h .map { |k, v| { 'x' => k, 'y' => v } } .to_json end
# File _plugins/gtn.rb, line 724 def unix_time_to_date(time) Time.at(time.to_i).strftime('%Y-%m-%d %H:%M:%S') end
# File _plugins/gtn.rb, line 75 def url_exists(url) cache.getset("url-exists-#{url}") do uri = URI.parse(url) http = Net::HTTP.new(uri.host, uri.port) http.use_ssl = true if uri.scheme == 'https' response = http.request_head(uri.path) #Jekyll.logger.warn response response.code == '200' end end