This is a wrapper for several Episode class objects.
Details
Lessons are made of up several episodes within the _episodes/
directory of
a lesson. This class keeps track of several episodes and allows us to switch
between RMarkdown and markdown episodes
Public fields
path
[
character
] path to Lesson directoryepisodes
[
list
] list of Episode class objects representing the episodes of the lesson.built
[
list
] list of Episode class objects representing the markdown artefacts rendered from RMarkdown files.extra
[
list
] list of Episode class objects representing the extra markdown components including index, setup, information for learners, information for instructors, and learner profiles. This is not processed for the jekyll lessons.sandpaper
[
logical
] whenTRUE
, the episodes in the lesson are written in pandoc flavoured markdown.FALSE
would indicate a jekyll-based lesson written in kramdown.rmd
[
logical
] whenTRUE
, the episodes represent RMarkdown files, default isFALSE
for markdown files (deprecated and unused).
Active bindings
n_problems
number of problems per episode
show_problems
contents of the problems per episode
files
the source files for each episode
Methods
Method new()
create a new Lesson object from a directory
Usage
Lesson$new(path = ".", rmd = FALSE, jekyll = TRUE, ...)
Arguments
path
[
character
] path to a lesson directory. This must have a folder called_episodes
within that contains markdown episodes. Defaults to the current working directory.rmd
[
logical
] whenTRUE
, the imported files will be the source RMarkdown files. Defaults toFALSE
, which reads the rendered markdown files.jekyll
[
logical
] whenTRUE
(default), the structure of the lesson is assumed to be derived from the carpentries/styles repository. WhenFALSE
, The structure is assumed to be a sandpaper lesson and extra content for learners, instructors, and profiles will be populated....
arguments passed on to Episode$new
Returns
a new Lesson object that contains a list of Episode objects in
$episodes
Examples
frg <- Lesson$new(lesson_fragment())
frg$path
frg$episodes
Method load_built()
read in the markdown content generated from RMarkdown sources and load load them into memory
Method get()
A getter for various active bindings in the Episode class of objects.
In practice this is syntactic sugar around
purrr::map(l$episodes, ~.x$element)
Arguments
element
[
character
] a defined element from the active bindings in the Episode class. Defaults to NULL, which will return nothing. Elements that do not exist in the Episode class will return NULLcollection
[
character
] one or more of "episodes" (default), "extra", or "built". SelectTRUE
to collect information from all files.
Examples
frg <- Lesson$new(lesson_fragment())
frg$get("error") # error code blocks
frg$get("links") # links
Method summary()
summary of element counts in each episode. This can be useful for assessing a broad overview of the lesson dynamics
Arguments
collection
[
character
] one or more of "episodes" (default), "extra", or "built". SelectTRUE
to collect information from all files.
Examples
frg <- Lesson$new(lesson_fragment())
frg$summary() # episode summary (default)
Method blocks()
Gather all of the blocks from the lesson in a list of xml_nodeset objects
Arguments
type
the type of block quote in the Jekyll syntax like ".challenge", ".discussion", or ".solution"
level
the level of the block within the document. Defaults to
0
, which represents all of the block_quotes within the document regardless of nesting level.path
[
logical
] ifTRUE
, the names of each element will be equivalent to the path. The default isFALSE
, which gives the name of each episode.body
the XML body of a carpentries lesson (an xml2 object)
Method challenges()
Gather all of the challenges from the lesson in a list of xml_nodeset objects
Arguments
path
[
logical
] ifTRUE
, the names of each element will be equivalent to the path. The default isFALSE
, which gives the name of each episode.graph
[
logical
] ifTRUE
, the output will be a data frame representing the directed graph of elements within the challenges. See theget_challenge_graph()
method in Episode.recurse
[
logical
] whengraph = TRUE
, this will include the solutions in the output. See Episode for more details.
Method thin()
Remove episodes that have no challenges
Arguments
verbose
[
logical
] ifTRUE
(default), the names of each episode removed is reported. Set toFALSE
to remove this behavior.
Examples
frg <- Lesson$new(lesson_fragment())
frg$thin()
Method reset()
Re-read all Episodes from disk
Examples
frg <- Lesson$new(lesson_fragment())
frg$episodes[[1]]$body
frg$isolate_blocks()$episodes[[1]]$body # empty
frg$reset()$episodes[[1]]$body # reset
Method isolate_blocks()
Remove all elements except for those within block quotes that have a kramdown tag. Note that this is a destructive process.
Examples
frg <- Lesson$new(lesson_fragment())
frg$isolate_blocks()$body # only one challenge block_quote
Method handout()
create a handout for all episodes in the lesson
Arguments
path
the path to the R Markdown file to be written. If
NULL
(default), no file will be written and the lines of the output document will be returned.solution
if
TRUE
solutions will be retained. Defaults toFALSE
Examples
lsn <- Lesson$new(lesson_fragment("sandpaper-fragment"), jekyll = FALSE)
cat(lsn$handout())
cat(lsn$handout(solution = TRUE))
Method validate_headings()
Validate that the heading elements meet minimum accessibility
requirements. See the internal validate_headings()
for deails.
This will validate the following aspects of all headings:
first heading starts at level 2 (
first_heading_is_second_level
)greater than level 1 (
greater_than_first_level
)increse sequentially (e.g. no jumps from 2 to 4) (
are_sequential
)have names (
have_names
)unique in their own hierarchy (
are_unique
)
Arguments
verbose
if
TRUE
, the heading tree will be printed to the console with any warnings assocated with the validators
Returns
a data frame with a variable number of rows and the follwoing columns:
episode the filename of the episode
heading the text from a heading
level the heading level
pos the position of the heading in the document
node the XML node that represents the heading
(the next five columns are the tests listed above)
path the path to the file.
Each row in the data frame represents an individual heading across the
Lesson. See validate_headings()
for more details.
Examples
frg <- Lesson$new(lesson_fragment())
frg$validate_headings()
Method validate_divs()
Validate that the divs are known. See the internal validate_divs()
for
details.
Arguments
verbose
if
TRUE
(default), Any failed tests will be printed to the console as a message giving information of where in the document the failing divs appear.
Returns
a wide data frame with five rows and the number of columns equal to the number of episodes in the lesson with an extra column indicating the type of validation. See the same method in the Episode class for details.
Examples
frg <- Lesson$new(lesson_fragment())
frg$validate_divs()
Method validate_links()
Validate that the links and images are valid and accessible. See the
internal validate_links()
for details.
Validation variables
External links use HTTPS (
enforce_https
)Internal links exist (
internal_okay
)External links are reachable (
all_reachable
) (planned)Images have alt text (
img_alt_text
)Link text is descriptive (
descriptive
)Link text is more than a single letter (
link_length
)
Arguments
verbose
if
TRUE
(default), Any failed tests will be printed to the console as a message giving information of where in the document the failing links/images appear.
Returns
a wide data frame with five rows and the number of columns equal to the number of episodes in the lesson with an extra column indicating the type of validation. See the same method in the Episode class for details.
Examples
frg <- Lesson$new(lesson_fragment())
frg$validate_links()
Examples
## ------------------------------------------------
## Method `Lesson$new`
## ------------------------------------------------
frg <- Lesson$new(lesson_fragment())
frg$path
#> [1] "/home/runner/work/_temp/Library/pegboard/lesson-fragment"
frg$episodes
#> $`10-lunch.md`
#> <Episode>
#> Inherits from: <yarn>
#> Public:
#> add_md: function (md, where = 0L)
#> body: xml_document, xml_node
#> challenges: active binding
#> clone: function (deep = FALSE)
#> code: active binding
#> confirm_sandpaper: function ()
#> error: active binding
#> get_blocks: function (type = NULL, level = 1L)
#> get_challenge_graph: function (recurse = TRUE)
#> get_divs: function (type = NULL, include = FALSE)
#> get_images: function (process = FALSE)
#> get_yaml: function ()
#> handout: function (path = NULL, solutions = FALSE)
#> head: function (n = 6L)
#> headings: active binding
#> images: active binding
#> initialize: function (path = NULL, process_tags = TRUE, fix_links = TRUE,
#> isolate_blocks: function ()
#> keypoints: active binding
#> label_divs: function ()
#> lesson: active binding
#> links: active binding
#> move_keypoints: function ()
#> move_objectives: function ()
#> move_questions: function ()
#> name: active binding
#> ns: http://commonmark.org/xml/1.0
#> objectives: active binding
#> output: active binding
#> path: /home/runner/work/_temp/Library/pegboard/lesson-fragment ...
#> protect_curly: function ()
#> protect_math: function ()
#> protect_unescaped: function ()
#> questions: active binding
#> remove_error: function ()
#> remove_output: function ()
#> reset: function ()
#> show: function ()
#> show_problems: active binding
#> solutions: active binding
#> summary: function ()
#> tags: active binding
#> tail: function (n = 6L)
#> unblock: function (token = "#'")
#> use_dovetail: function ()
#> use_sandpaper: function (rmd = FALSE, yml = list())
#> validate_divs: function (warn = TRUE)
#> validate_headings: function (verbose = TRUE, warn = TRUE)
#> validate_links: function (warn = TRUE)
#> warning: active binding
#> write: function (path = NULL, format = "md", edit = FALSE)
#> yaml: --- layout: break title: "Lunch" teaching: 0 exercises: ...
#> Private:
#> clear_yaml_item: function (what)
#> deep_clone: function (name, value)
#> encoding: UTF-8
#> md_lines: function (path = NULL, stylesheet = NULL)
#> mutations: FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
#> problems: list
#> record_problem: function (x)
#> sourcepos: TRUE
#>
#> $`12-for-loops.md`
#> <Episode>
#> Inherits from: <yarn>
#> Public:
#> add_md: function (md, where = 0L)
#> body: xml_document, xml_node
#> challenges: active binding
#> clone: function (deep = FALSE)
#> code: active binding
#> confirm_sandpaper: function ()
#> error: active binding
#> get_blocks: function (type = NULL, level = 1L)
#> get_challenge_graph: function (recurse = TRUE)
#> get_divs: function (type = NULL, include = FALSE)
#> get_images: function (process = FALSE)
#> get_yaml: function ()
#> handout: function (path = NULL, solutions = FALSE)
#> head: function (n = 6L)
#> headings: active binding
#> images: active binding
#> initialize: function (path = NULL, process_tags = TRUE, fix_links = TRUE,
#> isolate_blocks: function ()
#> keypoints: active binding
#> label_divs: function ()
#> lesson: active binding
#> links: active binding
#> move_keypoints: function ()
#> move_objectives: function ()
#> move_questions: function ()
#> name: active binding
#> ns: http://commonmark.org/xml/1.0
#> objectives: active binding
#> output: active binding
#> path: /home/runner/work/_temp/Library/pegboard/lesson-fragment ...
#> protect_curly: function ()
#> protect_math: function ()
#> protect_unescaped: function ()
#> questions: active binding
#> remove_error: function ()
#> remove_output: function ()
#> reset: function ()
#> show: function ()
#> show_problems: active binding
#> solutions: active binding
#> summary: function ()
#> tags: active binding
#> tail: function (n = 6L)
#> unblock: function (token = "#'")
#> use_dovetail: function ()
#> use_sandpaper: function (rmd = FALSE, yml = list())
#> validate_divs: function (warn = TRUE)
#> validate_headings: function (verbose = TRUE, warn = TRUE)
#> validate_links: function (warn = TRUE)
#> warning: active binding
#> write: function (path = NULL, format = "md", edit = FALSE)
#> yaml: --- title: "For Loops" teaching: 10 exercises: 15 questi ...
#> Private:
#> clear_yaml_item: function (what)
#> deep_clone: function (name, value)
#> encoding: UTF-8
#> md_lines: function (path = NULL, stylesheet = NULL)
#> mutations: FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
#> problems: list
#> record_problem: function (x)
#> sourcepos: TRUE
#>
#> $`14-looping-data-sets.md`
#> <Episode>
#> Inherits from: <yarn>
#> Public:
#> add_md: function (md, where = 0L)
#> body: xml_document, xml_node
#> challenges: active binding
#> clone: function (deep = FALSE)
#> code: active binding
#> confirm_sandpaper: function ()
#> error: active binding
#> get_blocks: function (type = NULL, level = 1L)
#> get_challenge_graph: function (recurse = TRUE)
#> get_divs: function (type = NULL, include = FALSE)
#> get_images: function (process = FALSE)
#> get_yaml: function ()
#> handout: function (path = NULL, solutions = FALSE)
#> head: function (n = 6L)
#> headings: active binding
#> images: active binding
#> initialize: function (path = NULL, process_tags = TRUE, fix_links = TRUE,
#> isolate_blocks: function ()
#> keypoints: active binding
#> label_divs: function ()
#> lesson: active binding
#> links: active binding
#> move_keypoints: function ()
#> move_objectives: function ()
#> move_questions: function ()
#> name: active binding
#> ns: http://commonmark.org/xml/1.0
#> objectives: active binding
#> output: active binding
#> path: /home/runner/work/_temp/Library/pegboard/lesson-fragment ...
#> protect_curly: function ()
#> protect_math: function ()
#> protect_unescaped: function ()
#> questions: active binding
#> remove_error: function ()
#> remove_output: function ()
#> reset: function ()
#> show: function ()
#> show_problems: active binding
#> solutions: active binding
#> summary: function ()
#> tags: active binding
#> tail: function (n = 6L)
#> unblock: function (token = "#'")
#> use_dovetail: function ()
#> use_sandpaper: function (rmd = FALSE, yml = list())
#> validate_divs: function (warn = TRUE)
#> validate_headings: function (verbose = TRUE, warn = TRUE)
#> validate_links: function (warn = TRUE)
#> warning: active binding
#> write: function (path = NULL, format = "md", edit = FALSE)
#> yaml: --- title: "Looping Over Data Sets" teaching: 5 exercise ...
#> Private:
#> clear_yaml_item: function (what)
#> deep_clone: function (name, value)
#> encoding: UTF-8
#> md_lines: function (path = NULL, stylesheet = NULL)
#> mutations: FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
#> problems: list
#> record_problem: function (x)
#> sourcepos: TRUE
#>
#> $`17-scope.md`
#> <Episode>
#> Inherits from: <yarn>
#> Public:
#> add_md: function (md, where = 0L)
#> body: xml_document, xml_node
#> challenges: active binding
#> clone: function (deep = FALSE)
#> code: active binding
#> confirm_sandpaper: function ()
#> error: active binding
#> get_blocks: function (type = NULL, level = 1L)
#> get_challenge_graph: function (recurse = TRUE)
#> get_divs: function (type = NULL, include = FALSE)
#> get_images: function (process = FALSE)
#> get_yaml: function ()
#> handout: function (path = NULL, solutions = FALSE)
#> head: function (n = 6L)
#> headings: active binding
#> images: active binding
#> initialize: function (path = NULL, process_tags = TRUE, fix_links = TRUE,
#> isolate_blocks: function ()
#> keypoints: active binding
#> label_divs: function ()
#> lesson: active binding
#> links: active binding
#> move_keypoints: function ()
#> move_objectives: function ()
#> move_questions: function ()
#> name: active binding
#> ns: http://commonmark.org/xml/1.0
#> objectives: active binding
#> output: active binding
#> path: /home/runner/work/_temp/Library/pegboard/lesson-fragment ...
#> protect_curly: function ()
#> protect_math: function ()
#> protect_unescaped: function ()
#> questions: active binding
#> remove_error: function ()
#> remove_output: function ()
#> reset: function ()
#> show: function ()
#> show_problems: active binding
#> solutions: active binding
#> summary: function ()
#> tags: active binding
#> tail: function (n = 6L)
#> unblock: function (token = "#'")
#> use_dovetail: function ()
#> use_sandpaper: function (rmd = FALSE, yml = list())
#> validate_divs: function (warn = TRUE)
#> validate_headings: function (verbose = TRUE, warn = TRUE)
#> validate_links: function (warn = TRUE)
#> warning: active binding
#> write: function (path = NULL, format = "md", edit = FALSE)
#> yaml: --- title: "Variable Scope" teaching: 10 exercises: 10 q ...
#> Private:
#> clear_yaml_item: function (what)
#> deep_clone: function (name, value)
#> encoding: UTF-8
#> md_lines: function (path = NULL, stylesheet = NULL)
#> mutations: FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
#> problems: list
#> record_problem: function (x)
#> sourcepos: TRUE
#>
## ------------------------------------------------
## Method `Lesson$get`
## ------------------------------------------------
frg <- Lesson$new(lesson_fragment())
frg$get("error") # error code blocks
#> $`10-lunch.md`
#> {xml_nodeset (0)}
#>
#> $`12-for-loops.md`
#> {xml_nodeset (2)}
#> [1] <code_block sourcepos="58:1-60:3" xml:space="preserve" name="" ktag="{: . ...
#> [2] <code_block sourcepos="70:1-75:3" xml:space="preserve" name="" ktag="{: . ...
#>
#> $`14-looping-data-sets.md`
#> {xml_nodeset (0)}
#>
#> $`17-scope.md`
#> {xml_nodeset (2)}
#> [1] <code_block sourcepos="37:1-42:3" xml:space="preserve" name="" ktag="{: . ...
#> [2] <code_block sourcepos="73:3-93:5" xml:space="preserve" name="" ktag="{: . ...
#>
frg$get("links") # links
#> $`10-lunch.md`
#> {xml_nodeset (0)}
#>
#> $`12-for-loops.md`
#> {xml_nodeset (1)}
#> [1] <link sourcepos="115:27-115:90" destination="https://docs.python.org/3/li ...
#>
#> $`14-looping-data-sets.md`
#> {xml_nodeset (8)}
#> [1] <link sourcepos="36:8-36:75" destination="https://docs.python.org/3/libra ...
#> [2] <link sourcepos="42:25-42:77" destination="https://docs.python.org/3/libr ...
#> [3] <link sourcepos="43:9-43:61" destination="https://docs.python.org/3/libra ...
#> [4] <link sourcepos="125:17-125:118" destination="https://pandas.pydata.org/p ...
#> [5] <link sourcepos="148:62-148:129" destination="https://docs.python.org/3/l ...
#> [6] <link xmlns="http://commonmark.org/xml/1.0" destination="{{ page.root }}/ ...
#> [7] <link xmlns="http://commonmark.org/xml/1.0" destination="{{ site.swc_page ...
#> [8] <link xmlns="http://commonmark.org/xml/1.0" destination="{{ page.root }}{ ...
#>
#> $`17-scope.md`
#> {xml_nodeset (0)}
#>
## ------------------------------------------------
## Method `Lesson$summary`
## ------------------------------------------------
frg <- Lesson$new(lesson_fragment())
frg$summary() # episode summary (default)
#> ! Summary not guaranteed for styles-based lessons
#> # A tibble: 4 × 12
#> page secti…¹ headi…² callo…³ chall…⁴ solut…⁵ code output warning error
#> <chr> <int> <int> <int> <int> <int> <int> <int> <int> <int>
#> 1 10-lunch.md 0 0 0 0 0 0 0 0 0
#> 2 12-for-loo… 24 24 7 7 10 32 4 0 2
#> 3 14-looping… 9 10 3 3 3 11 4 0 0
#> 4 17-scope.md 3 3 2 2 0 6 1 0 2
#> # … with 2 more variables: images <int>, links <int>, and abbreviated variable
#> # names ¹sections, ²headings, ³callouts, ⁴challenges, ⁵solutions
## ------------------------------------------------
## Method `Lesson$thin`
## ------------------------------------------------
frg <- Lesson$new(lesson_fragment())
frg$thin()
#> Removing 1 episode: 10-lunch.md
## ------------------------------------------------
## Method `Lesson$reset`
## ------------------------------------------------
frg <- Lesson$new(lesson_fragment())
frg$episodes[[1]]$body
#> {xml_document}
#> <document sourcepos="1:1-4:131" xmlns="http://commonmark.org/xml/1.0">
#> [1] <paragraph sourcepos="1:1-1:49">\n <text sourcepos="1:1-1:49" xml:space= ...
#> [2] <list sourcepos="2:1-4:131" type="bullet" tight="true">\n <item sourcepo ...
frg$isolate_blocks()$episodes[[1]]$body # empty
#> {xml_document}
#> <document sourcepos="1:1-4:131" xmlns="http://commonmark.org/xml/1.0">
frg$reset()$episodes[[1]]$body # reset
#> {xml_document}
#> <document sourcepos="1:1-4:131" xmlns="http://commonmark.org/xml/1.0">
#> [1] <paragraph sourcepos="1:1-1:49">\n <text sourcepos="1:1-1:49" xml:space= ...
#> [2] <list sourcepos="2:1-4:131" type="bullet" tight="true">\n <item sourcepo ...
## ------------------------------------------------
## Method `Lesson$isolate_blocks`
## ------------------------------------------------
frg <- Lesson$new(lesson_fragment())
frg$isolate_blocks()$body # only one challenge block_quote
#> NULL
## ------------------------------------------------
## Method `Lesson$handout`
## ------------------------------------------------
lsn <- Lesson$new(lesson_fragment("sandpaper-fragment"), jekyll = FALSE)
cat(lsn$handout())
#> ## Using RMarkdown
#>
#> ## Challenge 1: Can you do it?
#>
#> What is the output of this command?
#>
#> ```{r, eval=FALSE}
#> paste("This", "new", "template", "looks", "good")
#> ```
cat(lsn$handout(solution = TRUE))
#> ## Using RMarkdown
#>
#> ## Challenge 1: Can you do it?
#>
#> What is the output of this command?
#>
#> ```{r, eval=FALSE}
#> paste("This", "new", "template", "looks", "good")
#> ```
#>
#> :::::::::::::::::::::::: solution
#>
#> ## Output
#>
#> ```{r, echo=FALSE}
#> paste("This", "new", "template", "looks", "good")
#> ```
#>
#> ::::::::::::::::::::::::::::::::::
#>
#> ## Challenge 2: how do you nest solutions within challenge blocks?
#>
#> :::::::::::::::::::::::: solution
#>
#> You can add a line with at least three colons and a `solution` tag.
## ------------------------------------------------
## Method `Lesson$validate_headings`
## ------------------------------------------------
frg <- Lesson$new(lesson_fragment())
frg$validate_headings()
#> ── Heading structure ───────────────────────────────────────────────────────────
#> # Episode: “For Loops”
#> ├─## A for loop executes commands once for each value in a collection.
#> ├─## A for loop is made up of a collection, a loop variable, and a body.
#> ├─## The first line of the for loop must end with a colon, and the body must be
#> ├─## Loop variables can be called anything.
#> ├─## The body of a loop can contain many statements.
#> ├─## Use range to iterate over a sequence of numbers.
#> ├─## The Accumulator pattern turns many values into one.
#> ├─## Classifying Errors
#> ├─## Solution (duplicated)
#> ├─## Tracing Execution
#> ├─## Solution (duplicated)
#> ├─## Reversing a String
#> ├─## Solution (duplicated)
#> ├─## Practice Accumulating
#> ├─## Solution (duplicated)
#> ├─## Solution (duplicated)
#> ├─## Solution (duplicated)
#> ├─## Solution (duplicated)
#> ├─## Cumulative Sum
#> ├─## Solution (duplicated)
#> ├─## Identifying Variable Name Errors
#> ├─## Solution (duplicated)
#> ├─## Identifying Item Errors
#> └─## Solution (duplicated)
#> ────────────────────────────────────────────────────────────────────────────────
#> ── Heading structure ───────────────────────────────────────────────────────────
#> # Episode: “Looping Over Data Sets”
#> ├─## Use a for loop to process files given a list of their names.
#> ├─## Use glob.glob to find sets of files whose names match a pattern.
#> ├─## Use glob and for to process batches of files.
#> ├─## Determining Matches
#> ├─## Solution (duplicated)
#> ├─## Minimum File Size
#> ├─## Solution (duplicated)
#> ├─## Comparing Data
#> └─## Solution (duplicated)
#> └─### ZNK test links and images
#> ────────────────────────────────────────────────────────────────────────────────
#> ! There were errors in 13/37 headings
#> ◌ Headings must be unique
#> <https://webaim.org/techniques/semanticstructure/#headings>
#>
#> ::warning file=_episodes/12-for-loops.md,line=183:: (duplicated)
#> ::warning file=_episodes/12-for-loops.md,line=200:: (duplicated)
#> ::warning file=_episodes/12-for-loops.md,line=227:: (duplicated)
#> ::warning file=_episodes/12-for-loops.md,line=252:: (duplicated)
#> ::warning file=_episodes/12-for-loops.md,line=270:: (duplicated)
#> ::warning file=_episodes/12-for-loops.md,line=289:: (duplicated)
#> ::warning file=_episodes/12-for-loops.md,line=305:: (duplicated)
#> ::warning file=_episodes/12-for-loops.md,line=336:: (duplicated)
#> ::warning file=_episodes/12-for-loops.md,line=371:: (duplicated)
#> ::warning file=_episodes/12-for-loops.md,line=400:: (duplicated)
#> ::warning file=_episodes/14-looping-data-sets.md,line=119:: (duplicated)
#> ::warning file=_episodes/14-looping-data-sets.md,line=143:: (duplicated)
#> ::warning file=_episodes/14-looping-data-sets.md,line=162:: (duplicated)
## ------------------------------------------------
## Method `Lesson$validate_divs`
## ------------------------------------------------
frg <- Lesson$new(lesson_fragment())
frg$validate_divs()
## ------------------------------------------------
## Method `Lesson$validate_links`
## ------------------------------------------------
frg <- Lesson$new(lesson_fragment())
frg$validate_links()
#> ! There were errors in 4/14 links
#> ◌ Some linked internal files do not exist
#> ◌ Images need alt-text
#> <https://webaim.org/techniques/hypertext/link_text#alt_link>
#>
#> ::warning file=_episodes/14-looping-data-sets.md,line=191:: [missing file]
#> ../no-workie.svg
#> ::warning file=_episodes/14-looping-data-sets.md,line=195:: [image missing
#> alt-text] https://carpentries.org/assets/img/TheCarpentries.svg
#> ::warning file=_episodes/14-looping-data-sets.md,line=197:: [missing file]
#> ../no-workie.svg [image missing alt-text] ../no-workie.svg
#> ::warning file=_episodes/14-looping-data-sets.md,line=NA:: [image missing
#> alt-text] { page.root }/no-workie.svg