Episode | R Documentation |
Wrapper around an xml document to manipulate and inspect Carpentries episodes
The Episode class is a superclass of tinkr::yarn()
, which transforms
(commonmark-formatted) Markdown to XML and back again. The extension that
the Episode class provides is support for both Pandoc
and kramdown flavours of Markdown.
Read more about this class in vignette("intro-episode", package = "pegboard")
.
tinkr::yarn
-> Episode
children
[character
] a vector of absolute paths to child
files if they exist.
parents
[character
] a vector of absolute paths to immediate
parent files if they exist
build_parents
[character
] a vector of absolute paths to the
final parent files that will trigger this child file to build
show_problems
[list
] a list of all the problems that occurred in parsing the episode
headings
[xml_nodeset
] all headings in the document
links
[xml_nodeset
] all links (not images) in the document
images
[xml_nodeset
] all image sources in the document
tags
[xml_nodeset
] all the kramdown tags from the episode
questions
[character
] the questions from the episode
keypoints
[character
] the keypoints from the episode
objectives
[character
] the objectives from the episode
challenges
[xml_nodeset
] all the challenges blocks from the episode
solutions
[xml_nodeset
] all the solutions blocks from the episode
output
[xml_nodeset
] all the output blocks from the episode
error
[xml_nodeset
] all the error blocks from the episode
warning
[xml_nodeset
] all the warning blocks from the episode
code
[xml_nodeset
] all the code blocks from the episode
name
[character
] the name of the source file without the path
lesson
[character
] the path to the lesson where the episode is from
has_children
[logical
] an indicator of the presence of child
files (TRUE
) or their absence (FALSE
)
has_parents
[logical
] an indicator of the presence of parent
files (TRUE
) or their absence (FALSE
)
new()
Create a new Episode
Episode$new( path = NULL, process_tags = TRUE, fix_links = TRUE, fix_liquid = FALSE, parents = NULL, ... )
path
[character
] path to a markdown episode file on disk
process_tags
[logical
] if TRUE
(default), kramdown tags will
be processed into attributes of the parent nodes. If FALSE
, these
tags will be treated as text
fix_links
[logical
] if TRUE
(default), links pointing to
liquid tags (e.g. {{ page.root }}
) and included links (those supplied
by a call to {\% import links.md \%}
) will be appropriately processed
as valid links.
fix_liquid
[logical
] defaults to FALSE
, which means data is
immediately passed to tinkr::yarn. If TRUE
, all liquid variables
in relative links have spaces removed to allow the commonmark parser to
interpret them as links.
parents
[list
] a list of Episode
objects that represent the
immediate parents of this child
...
arguments passed on to tinkr::yarn and tinkr::to_xml()
A new Episode object with extracted XML data
scope <- Episode$new(file.path(lesson_fragment(), "_episodes", "17-scope.md")) scope$name scope$lesson scope$challenges
confirm_sandpaper()
enforce that the episode is a sandpaper episode withtout going through the conversion steps. The default Episodes from pegboard were assumed to be generated using Jekyll with kramdown syntax. This is a bit of a kludge to bypass the normal checks for kramdown syntax and just assume pandoc syntax
Episode$confirm_sandpaper()
get_blocks()
return all block_quote
elements within the Episode
Episode$get_blocks(type = NULL, level = 1L)
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 1
,
which represents all of the block_quotes are not nested within any other
block quotes. Increase the nubmer to increase the level of nesting.
[xml_nodeset
] all the blocks from the episode with the given
tag and level.
scope <- Episode$new(file.path(lesson_fragment(), "_episodes", "17-scope.md")) # get all the challenges scope$get_blocks(".challenge") # get the solutions scope$get_blocks(".solution", level = 2) \dontrun{ # download the source files for r-novice-gampinder into a Lesson object rng <- get_lesson("swcarpentry/r-novice-gapminder") dsp1 <- rng$episodes[["04-data-structures-part1.md"]] # There are 9 blocks in total dsp1$get_blocks() # One is a callout block dsp1$get_blocks(".callout") # One is a discussion block dsp1$get_blocks(".discussion") # Seven are Challenge blocks dsp1$get_blocks(".challenge") # There are eight solution blocks: dsp1$get_blocks(".solution", level = 2L) }
get_images()
fetch the image sources and optionally process them for easier parsing.
The default version of this function is equivalent to the active binding
$images
.
Episode$get_images(process = FALSE)
process
if TRUE
, images will be processed via the internal
function process_images()
, which will add the alt
attribute, if
available and extract img nodes from HTML blocks.
an xml_nodelist
loop <- Episode$new(file.path(lesson_fragment(), "_episodes", "14-looping-data-sets.md")) loop$get_images() loop$get_images(process = TRUE)
label_divs()
label all the div elements within the Episode to extract them with
$get_divs()
Episode$label_divs()
get_divs()
return all div elements within the Episode
Episode$get_divs(type = NULL, include = FALSE)
type
the type of div tag (e.g. 'challenge' or 'solution')
include
\[logical\]
if TRUE
, the div tags will be included in
the output. Defaults to FALSE
, which will only return the text between
the div tags.
get_yaml()
Extract the yaml metadata from the episode
Episode$get_yaml()
use_dovetail()
Ammend or add a setup code block to use {dovetail}
This will convert your lesson to use the dovetail R package for processing specialized block quotes which will do two things:
convert your lesson from md to Rmd
add to your setup chunk the following code
library('dovetail') source(dvt_opts())
If there is no setup chunk, one will be created. If there is a setup
chunk, then the source
and knitr_fig_path
calls will be removed.
Episode$use_dovetail()
use_sandpaper()
Use the sandpaper package for processing
This will convert your lesson to use the {sandpaper}
R package for
processing the lesson instead of Jekyll (default). Doing this will have
the following effects:
code blocks that were marked with liquid tags (e.g. {: .language-r}
are converted to standard code blocks or Rmarkdown chunks (with
language information at the top of the code block)
If rmarkdown is used and the lesson contains python code,
library('reticulate')
will be added to the setup chunk of the
lesson.
Episode$use_sandpaper(rmd = FALSE, yml = list())
rmd
if TRUE
, lessons will be converted to RMarkdown documents
yml
the list derived from the yml file for the episode
remove_error()
Remove error blocks
Episode$remove_error()
remove_output()
Remove output blocks
Episode$remove_output()
move_objectives()
move the objectives yaml item to the body
Episode$move_objectives()
move_keypoints()
move the keypoints yaml item to the body
Episode$move_keypoints()
move_questions()
move the questions yaml item to the body
Episode$move_questions()
get_challenge_graph()
Create a graph of the top-level elements for the challenges.
Episode$get_challenge_graph(recurse = TRUE)
recurse
if TRUE
(default), the content of the solutions will be
included in the graph; FALSE
will keep the solutions as block_quote
elements.
a data frame with four columns representing all the elements within the challenges in the Episode:
Block: The sequential number of the challenge block
from: the inward elements
to: the outward elements
pos: the position in the markdown document
Note that there are three special node names:
challenge: start or end of the challenge block
solution: start of the solution block
lesson: start of the lesson block
scope <- Episode$new(file.path(lesson_fragment(), "_episodes", "17-scope.md")) scope$get_challenge_graph()
show()
show the markdown contents on the screen
Episode$show(n = TRUE)
n
a subset of elements to show, default TRUE for all lines
a character vector with one line for each line of output
scope <- Episode$new(file.path(lesson_fragment(), "_episodes", "17-scope.md")) scope$head() scope$tail() scope$show()
head()
show the first n lines of markdown contents on the screen
Episode$head(n = 6L)
n
the number of lines to show from the top
a character vector with one line for each line of output
tail()
show the first n lines of markdown contents on the screen
Episode$tail(n = 6L)
n
the number of lines to show from the top
a character vector with one line for each line of output
write()
write the episode to disk as markdown
Episode$write(path = NULL, format = "md", edit = FALSE)
path
the path to write your file to. Defaults to an empty directory in your temporary folder
format
one of "md" (default) or "xml". This will create a file with the correct extension in the path
edit
if TRUE
, the file will open in an editor. Defaults to
FALSE
.
the episode object
scope <- Episode$new(file.path(lesson_fragment(), "_episodes", "17-scope.md")) scope$write()
handout()
Create a trimmed-down RMarkdown document that strips prose and contains only important code chunks and challenge blocks without solutions.
Episode$handout(path = NULL, solutions = FALSE)
path
(handout) a path to an R Markdown file to write. If this is
NULL
, no file will be written and the lines of the output will be
returned.
solutions
if TRUE
, include solutions in the output. Defaults to
FALSE
, which removes the solution blocks.
a character vector if path = NULL
, otherwise, it is called for
the side effect of creating a file.
lsn <- Lesson$new(lesson_fragment("sandpaper-fragment"), jekyll = FALSE) e <- lsn$episodes[[1]] cat(e$handout()) cat(e$handout(solution = TRUE))
reset()
Re-read episode from disk
Episode$reset()
the episode object
scope <- Episode$new(file.path(lesson_fragment(), "_episodes", "17-scope.md")) xml2::xml_text(scope$tags[1]) xml2::xml_set_text(scope$tags[1], "{: .code}") xml2::xml_text(scope$tags[1]) scope$reset() xml2::xml_text(scope$tags[1])
isolate_blocks()
Remove all elements except for those within block quotes that have a kramdown tag. Note that this is a destructive process.
Episode$isolate_blocks()
the Episode object, invisibly
scope <- Episode$new(file.path(lesson_fragment(), "_episodes", "17-scope.md")) scope$body # a full document with block quotes and code blocks, etc scope$isolate_blocks()$body # only one challenge block_quote
unblock()
convert challenge blocks to roxygen-like code blocks
Episode$unblock(token = "#'", force = FALSE)
token
the token to use to indicate non-code, Defaults to "#'"
force
force the conversion even if the conversion has already taken place
the Episode object, invisibly
loop <- Episode$new(file.path(lesson_fragment(), "_episodes", "14-looping-data-sets.md")) loop$body # a full document with block quotes and code blocks, etc loop$get_blocks() # all the blocks in the episode loop$unblock() loop$get_blocks() # no blocks loop$code # now there are two blocks with challenge tags
summary()
Get a high-level summary of the elements in the episode
Episode$summary()
a data frame with counts of the following elements per page:
sections: level 2 headings
headings: all headings
callouts: all callouts
challenges: subset of callouts
solutions: subset of callouts
code: all code block elements (excluding inline code)
output: subset of code that is displayed as output
warnining: subset of code that is displayed as a warning
error: subset of code that is displayed as an error
images: all images in markdown or HTML
links: all links in markdown or HTML
validate_headings()
perform validation on headings in a document.
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
)
Episode$validate_headings(verbose = TRUE, warn = TRUE)
verbose
if TRUE
(default), a message for each rule broken will
be issued to the stderr. if FALSE
, this will be silent.
warn
if TRUE
(default), a warning will be issued if there are
any failures in the tests.
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.
# Example: There are multiple headings called "Solution" that are not # nested within a higher-level heading and will throw an error loop <- Episode$new(file.path(lesson_fragment(), "_episodes", "14-looping-data-sets.md")) loop$validate_headings()
validate_divs()
perform validation on divs in a document.
This will validate the following aspects of divs. See validate_divs()
for details.
divs are of a known type (is_known
)
Episode$validate_divs(warn = TRUE)
warn
if TRUE
(default), a warning message will be if there are
any divs determined to be invalid. Set to FALSE
if you want the
table for processing later.
a logical TRUE
for valid divs and FALSE
for invalid
divs.
loop <- Episode$new(file.path(lesson_fragment(), "_episodes", "14-looping-data-sets.md")) loop$validate_divs()
validate_links()
perform validation on links and images in a document.
This will validate the following aspects of links. See validate_links()
for details.
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
)
Episode$validate_links(warn = TRUE)
warn
if TRUE
(default), a warning message will be if there are
any links determined to be invalid. Set to FALSE
if you want the
table for processing later.
a logical TRUE
for valid links and FALSE
for invalid
links.
loop <- Episode$new(file.path(lesson_fragment(), "_episodes", "14-looping-data-sets.md")) loop$validate_links()
clone()
The objects of this class are cloneable with this method.
Episode$clone(deep = FALSE)
deep
Whether to make a deep clone.
The current XLST spec for tinkr does not support kramdown, which the Carpentries Episodes are styled with, thus some block tags will be destructively modified in the conversion.
## ------------------------------------------------
## Method `Episode$new`
## ------------------------------------------------
scope <- Episode$new(file.path(lesson_fragment(), "_episodes", "17-scope.md"))
scope$name
scope$lesson
scope$challenges
## ------------------------------------------------
## Method `Episode$get_blocks`
## ------------------------------------------------
scope <- Episode$new(file.path(lesson_fragment(), "_episodes", "17-scope.md"))
# get all the challenges
scope$get_blocks(".challenge")
# get the solutions
scope$get_blocks(".solution", level = 2)
## Not run:
# download the source files for r-novice-gampinder into a Lesson object
rng <- get_lesson("swcarpentry/r-novice-gapminder")
dsp1 <- rng$episodes[["04-data-structures-part1.md"]]
# There are 9 blocks in total
dsp1$get_blocks()
# One is a callout block
dsp1$get_blocks(".callout")
# One is a discussion block
dsp1$get_blocks(".discussion")
# Seven are Challenge blocks
dsp1$get_blocks(".challenge")
# There are eight solution blocks:
dsp1$get_blocks(".solution", level = 2L)
## End(Not run)
## ------------------------------------------------
## Method `Episode$get_images`
## ------------------------------------------------
loop <- Episode$new(file.path(lesson_fragment(), "_episodes", "14-looping-data-sets.md"))
loop$get_images()
loop$get_images(process = TRUE)
## ------------------------------------------------
## Method `Episode$get_challenge_graph`
## ------------------------------------------------
scope <- Episode$new(file.path(lesson_fragment(), "_episodes", "17-scope.md"))
scope$get_challenge_graph()
## ------------------------------------------------
## Method `Episode$show`
## ------------------------------------------------
scope <- Episode$new(file.path(lesson_fragment(), "_episodes", "17-scope.md"))
scope$head()
scope$tail()
scope$show()
## ------------------------------------------------
## Method `Episode$write`
## ------------------------------------------------
scope <- Episode$new(file.path(lesson_fragment(), "_episodes", "17-scope.md"))
scope$write()
## ------------------------------------------------
## Method `Episode$handout`
## ------------------------------------------------
lsn <- Lesson$new(lesson_fragment("sandpaper-fragment"), jekyll = FALSE)
e <- lsn$episodes[[1]]
cat(e$handout())
cat(e$handout(solution = TRUE))
## ------------------------------------------------
## Method `Episode$reset`
## ------------------------------------------------
scope <- Episode$new(file.path(lesson_fragment(), "_episodes", "17-scope.md"))
xml2::xml_text(scope$tags[1])
xml2::xml_set_text(scope$tags[1], "{: .code}")
xml2::xml_text(scope$tags[1])
scope$reset()
xml2::xml_text(scope$tags[1])
## ------------------------------------------------
## Method `Episode$isolate_blocks`
## ------------------------------------------------
scope <- Episode$new(file.path(lesson_fragment(), "_episodes", "17-scope.md"))
scope$body # a full document with block quotes and code blocks, etc
scope$isolate_blocks()$body # only one challenge block_quote
## ------------------------------------------------
## Method `Episode$unblock`
## ------------------------------------------------
loop <- Episode$new(file.path(lesson_fragment(), "_episodes", "14-looping-data-sets.md"))
loop$body # a full document with block quotes and code blocks, etc
loop$get_blocks() # all the blocks in the episode
loop$unblock()
loop$get_blocks() # no blocks
loop$code # now there are two blocks with challenge tags
## ------------------------------------------------
## Method `Episode$validate_headings`
## ------------------------------------------------
# Example: There are multiple headings called "Solution" that are not
# nested within a higher-level heading and will throw an error
loop <- Episode$new(file.path(lesson_fragment(), "_episodes", "14-looping-data-sets.md"))
loop$validate_headings()
## ------------------------------------------------
## Method `Episode$validate_divs`
## ------------------------------------------------
loop <- Episode$new(file.path(lesson_fragment(), "_episodes", "14-looping-data-sets.md"))
loop$validate_divs()
## ------------------------------------------------
## Method `Episode$validate_links`
## ------------------------------------------------
loop <- Episode$new(file.path(lesson_fragment(), "_episodes", "14-looping-data-sets.md"))
loop$validate_links()
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.