Skip to content
Snippets Groups Projects
Commit 7bb0f777 authored by Greg Wilson's avatar Greg Wilson
Browse files

Merging with gh-pages

parents f8fcac48 5741e585
Branches
Tags
No related merge requests found
......@@ -13,7 +13,7 @@
<div class="span10 offset1">
<h1 class="title">$title$</h1>
$if(subtitle)$<h2 class="subtitle">$subtitle$</h2>$endif$
$body$
$body$
</div>
</div>
$footer$
......
......@@ -33,7 +33,7 @@
<div class="deck-container">
<!-- Begin slides. Just make elements with a class of slide. -->
$body$
$body$
<!-- End slides. -->
<!-- Begin extension snippets. Add or remove as needed. -->
......
......@@ -4,7 +4,7 @@ title: Lesson Title
subtitle: Topic Title One
minutes: 10
---
> ## Learning Objectives {.objectives}
> ## Learning Objectives
>
> * Learning objective 1
> * Learning objective 2
......
......@@ -4,7 +4,7 @@ title: Lesson Title
subtitle: Topic Title Two
minutes: 10
---
> ## Learning Objectives {.objectives}
> ## Learning Objectives
>
> * Learning objective 1
> * Learning objective 2
......
#!/usr/bin/env python
"""Pandoc filter to convert Blockquotes with attributes into Div
with attributes.
......@@ -7,10 +8,11 @@ Usage:
A blockquote will be converted if
1. it begins with a header
2. that header has attributes
3. those attributes contain a single class
4. that class is one of ['objectives', 'callout', 'challenge']
1. it begins with a header
2. that either
1. matches "Prerequisites", "Objectives", "Callout" or "Challenge" OR
2. has attributes containing a single class matching
one of ['prereq', 'objectives', 'callout', 'challenge']
For example, this is a valid blockquote:
......@@ -24,6 +26,18 @@ and it will be converted into this markdown:
Let's do something.
</div>
This is also a valid blockquote:
> ## Prerequisites
> Breakfast!
and it will be converted into this markdown:
<div class='prereq'>
## Prerequisites
Breakfast!
</div>
For debugging purposes you may find it useful to test the filter
like this:
......@@ -33,10 +47,21 @@ like this:
import pandocfilters as pf
valid_classes = ['objectives', 'callout', 'challenge']
# These are classes that, if set on the title of a blockquote, will
# trigger the blockquote to be converted to a div.
SPECIAL_CLASSES = ['callout', 'challenge', 'prereq', 'objectives']
# These are titles of blockquotes that will cause the blockquote to
# be converted into a div. They are 'title': 'class' pairs, where the
# 'title' will create a blockquote with the corresponding 'class'.
SPECIAL_TITLES = {'prerequisites': 'prereq',
'learning objectives': 'objectives',
'objectives': 'objectives',
'challenge': 'challenge',
'callout': 'callout'}
def find_attributes(blockquote):
def find_header(blockquote):
"""Find attributes in a blockquote if they are defined on a
header that is the first thing in the block quote.
......@@ -45,7 +70,7 @@ def find_attributes(blockquote):
"""
if blockquote[0]['t'] == 'Header':
level, attr, inline = blockquote[0]['c']
return attr
return level, attr, inline
def remove_attributes(blockquote):
......@@ -70,10 +95,21 @@ def blockquote2div(key, value, format, meta):
"""
if key == 'BlockQuote':
blockquote = value
attr = find_attributes(blockquote)
if not attr:
header = find_header(blockquote)
if not header:
return
elif len(attr[1]) == 1 and attr[1][0] in valid_classes:
else:
level, attr, inlines = header
id, classes, kvs = attr
ltitle = pf.stringify(inlines).lower()
if ltitle in SPECIAL_TITLES:
classes.append(SPECIAL_TITLES[ltitle])
return pf.Div(attr, blockquote)
elif len(classes) == 1 and classes[0] in SPECIAL_CLASSES:
remove_attributes(blockquote)
# a blockquote is just a list of blocks, so it can be
# passed directly to Div, which expects Div(attr, blocks)
......
#!/usr/bin/env bash
#!/usr/bin/python
#
# Software Carpentry Lesson Validator
#
# Check for errors in lessons built using the Software Carpentry template
# found at http://github.com/swcarpentry/lesson-template.
#
# Usage:
#
# $ tools/check
# Placeholder for actual conformance checking script (which will
# probably be Python, not Bash).
import sys
import os
import re
import yaml
grep -i -n 'FIX''ME' $*
#----------------------------------------
# Error reporting.
def report_error(file_path, line_number, line, error_message):
"""
Print information about general error.
"""
ERR_MSG = "Error at line {} of {}:\n\t{}\n{}"
print(ERR_MSG.format(line_number, file_path, line, error_message))
def report_missing(present, file_path, missing_element):
"""
Print information about missing element.
"""
ERR_MSG = "Error on {}: missing {}"
if not present:
print(ERR_MSG.format(file_path, missing_element))
def report_missing_metadata(missing_element):
"""
Print information about missing metadata at YAML header.
"""
ERR_MSG = "Error on YAML header: missing {}"
print(ERR_MSG.format(missing_element))
def report_broken_link(file_path, line_number, link):
"""
Print information about broken link.
"""
ERR_MSG = "Broken link at line {} of {}:\n\tCan't find {}."
print(ERR_MSG.format(line_number, file_path, link))
#----------------------------------------
# Checking.
def check_yaml(metadata):
"""
Check if all metadata are present at YAML header.
"""
METADATA_REQUIRED = {"layout", "title", "minutes"}
for key in METADATA_REQUIRED - set(metadata.keys()):
report_missing_metadata(key)
# TODO: Implement check_lesson
def check_lesson(file_path):
"""
Checks the file ``pages/[0-9]{2}-.*.md`` for:
- "layout: topic" in YAML header
- "title" as keyword in YAML header
- line "> ## Learning Objectives {.objectives}" after YAML header
- items in learning objectives begin with "*"
- items in learning objective following four-space indentation rule
- code samples be of type input, error, output, python, shell, r, matlab, or sql
- callout box style
- challenge box style
"""
pass
# TODO: Implement check_discussion
def check_discussion(file_path):
"""
Checks the file ``pages/discussion.md`` for:
FIXME: tell what need to check.
"""
pass
# TODO: Complete implementation of check_index
# TODO: break check_index into pieces -- it's too long.
def check_index(file_path):
"""
Checks the file ``pages/index.md`` for:
- "layout: lesson" in YAML header
- "title" as keyword in YAML header
- introductory paragraph(s) right after YAML header
- line with "> ## Prerequisites"
- non-empty prerequisites
- title line with "## Topics"
- items at topic list begin with "*"
- items in topic list follow four-space indentation rule
- links at topic list are valid
- line with "## Other Resources"
- items at other resources list begin with "*"
- link at other resources list are valid
"""
# State variables
in_yaml = False
yaml_metadata = []
has_prerequisites = False
has_topics = False
has_other_resources = False
# Load file and process it
with open(file_path, "r") as lines:
for line_number, line in enumerate(lines):
if re.match("---", line): # what if there are multiple YAML blocks??
in_yaml = not in_yaml
elif in_yaml:
yaml_metadata.append(line)
elif re.match("> ## Prerequisites", line): # check this in the Markdown or in the generated HTML?
has_prerequisites = True
elif re.match("## Topics", line): # as above?
has_topics = True
elif re.match("## Other Resources", line): # as above
has_other_resources = True
else:
## Push this check into another function - this one is getting too long.
# Check if local links are valid
matches = re.search("\[.*\]\((?P<link>.*)\)", line)
if matches and not matches.group("link").startswith("http"):
link = os.path.join(os.path.dirname(file_path), matches.group("link"))
if link.endswith(".html"):
link = link.replace("html", "md") # NO: what about "03-html-editing.html" ?
if not os.path.exists(link):
report_broken_link(file_path, line_number, link)
## Again, this function is too long - break it into sub-functions.
# Check YAML
yaml_metadata = yaml.load("\n".join(yaml_metadata))
check_yaml(yaml_metadata)
# Check sections
## Note the refactoring: replaces three conditionals with one.
report_missing(has_prerequisites, file_path, "Prerequisites")
report_missing(has_topics, file_path, "Topics")
report_missing(has_other_resources, file_path, "Other Resources")
# TODO Implement check_intructors
def check_intructors(file_path):
"""
Checks the file ``pages/instructors.md`` for:
- "title: Instructor"s Guide" in YAML header
- line with "## Overall"
- line with "## General Points"
- lines with topics titles begin with "## "
- points begin with "*" and following four space rules.
"""
pass
# TODO Implement check_motivation
def check_motivation(file_path):
"""
Checks the file ``pages/motivation.md``.
FIXME: tell what need to check.
"""
pass
# TODO Implement check_reference
def check_reference(file_path):
"""
Checks the file ``pages/reference.md`` for:
- ``layout: reference`` in YAML header
- line with "## Glossary"
- words definitions after at the "Glossary" as::
> **Key Word 1**: the definition
> relevant to the lesson.
"""
pass
def check_file(file_path):
"""
Call the correctly check function based on the name of the file.
"""
# Pair of regex and function to call
CONTROL = (
("[0-9]{2}-.*", check_lesson),
("discussion", check_discussion),
("index", check_index),
("instructors", check_intructors),
("motivation", check_motivation),
("reference", check_reference)
)
for (pattern, checker) in CONTROL:
if re.search(pattern, file_path):
checker(file_path)
def main(list_of_files):
"""
Call the check function for every file in ``list_of_files``.
If ``list_of_files`` is empty load all the files from ``pages`` directory.
"""
if not list_of_files:
list_of_files = [os.path.join("pages", filename) for filename in os.listdir("pages")]
for filename in list_of_files:
if filename.endswith(".md"):
check_file(filename)
if __name__ == "__main__":
main(sys.argv[1:])
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment