diff --git a/_layouts/learning-pathway.html b/_layouts/learning-pathway.html
index e2079e7d3a2d93..aedb9208b4b301 100644
--- a/_layouts/learning-pathway.html
+++ b/_layouts/learning-pathway.html
@@ -44,6 +44,23 @@
{{ section.section }}
{% if section.tutorials %}
{% assign material_list = site | convert_to_material_list: section.tutorials %}
+ {% icon time aria=false %} {{ locale['time-estimation'] | default: "Time estimation"}}: {{ material_list | sum_duration }}
+
+ Learning Objectives
+
+ {% if section.learning_objectives %}
+ {% for lo in section.learning_objectives %}
+ - {{ lo }}
+ {% endfor %}
+ {% else %}
+ {% for material in material_list %}
+ {% for lo in material.objectives %}
+ - {{ lo }}
+ {% endfor %}
+ {% endfor %}
+ {% endif %}
+
+
{% include _includes/tutorial_list.html sub=material_list %}
{% endif %}
{% endfor %}
diff --git a/_plugins/jekyll-duration.rb b/_plugins/jekyll-duration.rb
index a0a1f8b225d309..315229d916c3a9 100644
--- a/_plugins/jekyll-duration.rb
+++ b/_plugins/jekyll-duration.rb
@@ -14,14 +14,15 @@ module DurationFilter
# {{ "T1H30M" | duration_to_human }}
# => "1 hour 30 minutes"
def duration_to_human(duration)
- # Match the different parts of the string, must match entire string or it will fail.
- match = /^(?:([0-9]*)[Hh])*(?:([0-9]*)[Mm])*(?:([0-9.]*)[Ss])*$/.match(duration)
-
- # If it doesn't match, pass through unedited so we don't cause unexpected issues.
- if match.nil?
- puts "Could not parse time: #{duration}"
+ seconds = parse_rfc3339(duration)
+ if seconds.nil?
return duration
end
+ return fmt_duration(seconds)
+ end
+
+ def fmt_duration(seconds)
+ d = resolve_hms(seconds)
# Otherwise append english terms for the various parts
duration_parts = []
@@ -37,22 +38,85 @@ def duration_to_human(duration)
end
# Hours
- if !match[1].nil?
- if match[1] == '1'
- duration_parts.push("#{match[1]} " + hour)
+ if d[:hours] > 0
+ if d[:hours] == 1
+ duration_parts.push("#{d[:hours]} " + hour)
else
- duration_parts.push("#{match[1]} " + hours)
+ duration_parts.push("#{d[:hours]} " + hours)
end
end
# Minutes - assuming no one uses `1 minute`
- duration_parts.push("#{match[2]} " + minutes) if !match[2].nil?
+ duration_parts.push("#{d[:minutes]} " + minutes) if d[:minutes] > 0
# Hopefully no one uses seconds
- duration_parts.push("#{match[3]} seconds") if !match[3].nil?
+ duration_parts.push("#{d[:seconds]} seconds") if d[:seconds] > 0
duration_parts.join(' ')
end
+
+ ##
+ # Sum the durations correctly for multiple RFC3339 formatted durations.
+ # Params:
+ # +s+:: The RFC3339 formatted duration string
+ # Returns:
+ # +d+:: a number of seconds
+ def parse_rfc3339(s)
+ if s == 0
+ return 0
+ end
+
+ # Match the different parts of the string, must match entire string or it
+ # will fail.
+ match = /^T?(?:([0-9]*)[Hh])*(?:([0-9]*)[Mm])*(?:([0-9.]*)[Ss])*$/.match(s)
+
+ # If it doesn't match, pass through unedited so we don't cause unexpected
+ # issues.
+ if match.nil?
+ Jekyll.logger.debug "[GTN/Durations]:", "Could not parse time: #{s}"
+ return nil
+ end
+
+ return match[1].to_i * 3600 + match[2].to_i * 60 + match[3].to_i
+ end
+
+ ##
+ # Turn a count of seconds into hours/minutes/seconds.
+ # Params:
+ # +Int+:: A number of seconds
+ # Returns:
+ # +Hash+:: A hash with keys for hours, minutes, and seconds
+ #
+ # Example:
+ # resolve_hms(5400)
+ # => { hours: 1, minutes: 30, seconds: 0 }
+ def resolve_hms(seconds)
+ # Normalize the total
+ minutes = seconds / 60
+ seconds = seconds % 60
+ hours = minutes / 60
+ minutes = minutes % 60
+
+ { hours: hours, minutes: minutes, seconds: seconds }
+ end
+
+ ##
+ # Sum the durations correctly for multiple RFC3339 formatted durations.
+ # Params:
+ # +materials+:: The GTN material objects
+ # Returns:
+ # +String+:: The human total duration
+ def sum_duration(materials)
+ Jekyll.logger.debug "[GTN/Durations]: sum durations with #{materials.length} materials."
+ total = 0
+ materials.each do |material|
+ if ! material['time_estimation'].nil?
+ Jekyll.logger.debug " [GTN/Durations]: #{material['time_estimation']} #{material['title']} -> #{parse_rfc3339(material['time_estimation'])}"
+ total += parse_rfc3339(material['time_estimation'])
+ end
+ end
+ fmt_duration(total)
+ end
end
end
diff --git a/bin/schema-learning-pathway.yaml b/bin/schema-learning-pathway.yaml
index 5c18932b46f7de..75616a9c192135 100644
--- a/bin/schema-learning-pathway.yaml
+++ b/bin/schema-learning-pathway.yaml
@@ -107,6 +107,23 @@ mapping:
required: true
description:
type: str
+ learning_objectives:
+ type: seq
+ sequence:
+ - type: str
+ required: true
+ description: |
+ List of Specific, Measurable, Achievable, Relevant, and Time-bound (SMART) learning objectives for the tutorial
+
+ A learning objective is a single sentence describing what a learner will be able to do once they have done the tutorial. Generally it is best to follow a 2C or 3C learning objective such as:
+
+ - Compute (Skill)
+ - multiple whole genome assemblies (Objective)
+ - in such a way to develop big data processing skills (Result)
+ _examples:
+ - Understand the basic concepts behind phylogenetic trees, as applied to *Mycobacterium tuberculosis*
+ - Explore Biodiversity data with taxonomic, temporal and geographical informations
+ - Generate a DotPlot emulating the original paper using a different analysis tool
tutorials:
type: seq
sequence:
diff --git a/bin/schema-tutorial.yaml b/bin/schema-tutorial.yaml
index 8c618393fa12bc..53e4ebfc5d8bb5 100644
--- a/bin/schema-tutorial.yaml
+++ b/bin/schema-tutorial.yaml
@@ -35,7 +35,7 @@ mapping:
- type: str
required: true
description: |
- list of learning objectives for the tutorial
+ List of Specific, Measurable, Achievable, Relevant, and Time-bound (SMART) learning objectives for the tutorial
A learning objective is a single sentence describing what a learner will be able to do once they have done the tutorial. Generally it is best to follow a 2C or 3C learning objective such as: