Newer
Older
#!/usr/bin/python
#
# Software Carpentry Lesson Validator
#
# Check for errors at Software Carpentry lessons.
#
# Usage:
#
# $ tools/check
#----------------------------------------
# Error reporting.
def report_error(file_path, line_number, line, error_message):
'''
FIXME: docstring.
'''
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):
'''
FIXME: docstring.
'''
ERR_MSG = "Error on {}: missing {}"
if not present:
print(ERR_MSG.format(file_path, missing_element))
'''
FIXME: docstring.
'''
ERR_MSG = "Error on YAML header: missing {}"
print(ERR_MSG.format(missing_element))
def report_broken_link(file_path, line_number, link):
'''
FIXME: docstring.
'''
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)
'''
FIXME: docstring telling people what you want them to write here.
'''
'''
FIXME: docstring telling people what you want them to write here.
'''
'''
FIXME: docstring.
And break this up into pieces -- it's too long.
'''
# 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?
elif re.match('## Other Resources', line): # as above
## 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"))
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))
## 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")
'''
FIXME: docstring telling people what you want them to write here.
'''
'''
FIXME: docstring telling people what you want them to write here.
'''
'''
FIXME: docstring telling people what you want them to write here.
'''
'''
FIXME: docstring telling people what you want them to write here.
'''
## Functions are objects, and so can be put in tables like the one below.
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)
## main doesn't take sys.argv[1:] or the like? Will help with testing...
'''
FIXME: docstring telling people what you want them to write here.
'''
lessons_file = os.listdir("pages")
for lesson in lessons_file:
if lesson.endswith('.md'):
check_file('pages/{}'.format(lesson))
if __name__ == "__main__":
main()