diff --git a/tools/check b/tools/check index 8f3241cf7eae5679cab5577441bbcc40710b9cd1..cef91e4439f0aaf6f1bc7a5c2e9a92d5a2314e75 100755 --- a/tools/check +++ b/tools/check @@ -10,37 +10,39 @@ import os import re +import sys + import yaml #---------------------------------------- # Error reporting. def report_error(file_path, line_number, line, error_message): - ''' - FIXME: docstring. - ''' + """ + 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): - ''' - FIXME: docstring. - ''' + """ + 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): - ''' - FIXME: docstring. - ''' + """ + 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): - ''' - FIXME: docstring. - ''' + """ + 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)) @@ -55,23 +57,50 @@ def check_yaml(metadata): for key in METADATA_REQUIRED - set(metadata.keys()): report_missing_metadata(key) +# TODO: Implement check_lesson def check_lesson(file_path): - ''' - FIXME: docstring telling people what you want them to write here. - ''' + """ + Checks the file ``pages/[0-9]{2}-.*.md`` for: + + - "layout: topic" at YAML header + - "title" as keyword at YAML header + - line "> ## Learning Objectives {.objectives}" after YAML header + - items at learning objectives begin with "*" + - items at learning objective following four space rule + - code samples be of type error, output, python, shell, r, matlab, sql + - callout box style + - line with "> ## Key Points {.keypoints}" + - challenge box style + """ pass +# TODO: Implement check_discussion def check_discussion(file_path): - ''' - FIXME: docstring telling people what you want them to write here. - ''' + """ + 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): - ''' - FIXME: docstring. - And break this up into pieces -- it's too long. - ''' + """ + Checks the file ``pages/index.md`` for: + + - "layout: lesson" in YAML header + - "title" as keyword in YAML header + - introductory paragraph right after YAML header + - line with "> ## Prerequisites" + - non empty prerequisites + - line with "## Topics" + - items at topic list begin with "*" + - 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 = [] @@ -80,33 +109,33 @@ def check_index(file_path): has_other_resources = False # Load file and process it - with open(file_path, 'r') as lines: + 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?? + 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("> ## Prerequisites", line): # check this in the Markdown or in the generated HTML? has_prerequisites = True - elif re.match('## Topics', line): # as above? + elif re.match("## Topics", line): # as above? has_topics = True - elif re.match('## Other Resources', line): # as above + 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.*)\)', line) + matches = re.search("\[.*\]\((?P.*)\)", 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' ? + 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, {"minutes"}) + yaml_metadata = yaml.load("\n".join(yaml_metadata)) + check_yaml(yaml_metadata) # Check sections ## Note the refactoring: replaces three conditionals with one. @@ -114,34 +143,52 @@ def check_index(file_path): 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): - ''' - FIXME: docstring telling people what you want them to write here. - ''' + """ + 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): - ''' - FIXME: docstring telling people what you want them to write here. - ''' + """ + Checks the file ``pages/motivation.md``. + + FIXME: tell what need to check. + """ pass +# TODO Implement check_reference def check_reference(file_path): - ''' - FIXME: docstring telling people what you want them to write here. - ''' + """ + 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): - ''' - 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. + """ + 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), + ("[0-9]{2}-.*", check_lesson), + ("discussion", check_discussion), + ("index", check_index), + ("instructors", check_intructors), ("motivation", check_motivation), ("reference", check_reference) ) @@ -149,16 +196,18 @@ def check_file(file_path): if re.search(pattern, file_path): checker(file_path) -## main doesn't take sys.argv[1:] or the like? Will help with testing... -def main(): - ''' - 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'): - ## Why not os.path.join('pages', lesson) ? - check_file('pages/{}'.format(lesson)) +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() + main(sys.argv[1:])