|
1 | | -#!/usr/bin/env bash |
| 1 | +#!/usr/bin/python |
| 2 | +# |
| 3 | +# Software Carpentry Lesson Validator |
| 4 | +# |
| 5 | +# Check for errors at Software Carpentry lessons. |
| 6 | +# |
| 7 | +# Usage: |
| 8 | +# |
| 9 | +# $ tools/check |
2 | 10 |
|
3 | | -# Placeholder for actual conformance checking script (which will |
4 | | -# probably be Python, not Bash). |
| 11 | +import os |
| 12 | +import os.path |
| 13 | +import re |
5 | 14 |
|
6 | | -grep -i -n 'FIX''ME' $* |
| 15 | +import yaml |
| 16 | + |
| 17 | +def report_error(file_path, line_number, line, error_message): |
| 18 | + print("Error at line {} of {}:\n\t{}\n{}".format(line_number, |
| 19 | + file_path, line, error_message)) |
| 20 | + |
| 21 | +def report_missing(file_path, missing_element): |
| 22 | + print("Error on {}: missing {}".format(file_path, missing_element)) |
| 23 | + |
| 24 | +def report_missing_metadata(missing_element): |
| 25 | + print("Error on YAML header: missing {}".format(missing_element)) |
| 26 | + |
| 27 | +def report_broken_link(file_path, line_number, link): |
| 28 | + print("Broken link at line {} of {}:\n\tCan't find {}.".format(line_number, |
| 29 | + file_path, link)) |
| 30 | + |
| 31 | +def check_yaml(metadata, skip=[]): |
| 32 | + """ |
| 33 | + Check if all metadata are present at YAML header. |
| 34 | +
|
| 35 | + :param skip: list of keys to skip check |
| 36 | + """ |
| 37 | + metadata_required = ["layout", "title", "minutes"] |
| 38 | + for key in metadata_required: |
| 39 | + if key not in skip and key not in metadata: |
| 40 | + report_missing_metadata(key) |
| 41 | + |
| 42 | +def check_lesson(file_path): |
| 43 | + pass |
| 44 | + |
| 45 | +def check_discussion(file_path): |
| 46 | + pass |
| 47 | + |
| 48 | +def check_index(file_path): |
| 49 | + # State variables |
| 50 | + in_yaml = False |
| 51 | + yaml_metadata = [] |
| 52 | + has_prerequisites = False |
| 53 | + has_topics = False |
| 54 | + has_other_resources = False |
| 55 | + |
| 56 | + # Load file and process it |
| 57 | + with open(file_path, 'r') as lines: |
| 58 | + for line_number, line in enumerate(lines): |
| 59 | + if re.match('---', line): |
| 60 | + in_yaml = not in_yaml |
| 61 | + elif in_yaml: |
| 62 | + yaml_metadata.append(line) |
| 63 | + elif re.match('> ## Prerequisites', line): |
| 64 | + has_prerequisites = True |
| 65 | + elif re.match('## Topics', line): |
| 66 | + has_topics = True |
| 67 | + elif re.match('## Other Resources', line): |
| 68 | + has_other_resources = True |
| 69 | + else: |
| 70 | + # Check if local links are valid |
| 71 | + matches = re.search('\[.*\]\((?P<link>.*)\)', line) |
| 72 | + if matches and not matches.group("link").startswith("http"): |
| 73 | + link = os.path.join(os.path.dirname(file_path), |
| 74 | + matches.group("link")) |
| 75 | + if link.endswith(".html"): |
| 76 | + link = link.replace("html", "md") |
| 77 | + if not os.path.exists(link): |
| 78 | + report_broken_link(file_path, line_number, link) |
| 79 | + |
| 80 | + # Check YAML |
| 81 | + yaml_metadata = yaml.load('\n'.join(yaml_metadata)) |
| 82 | + check_yaml(yaml_metadata, ["minutes"]) |
| 83 | + |
| 84 | + # Check sections |
| 85 | + if not has_prerequisites: |
| 86 | + report_missing(file_path, "Prerequisites") |
| 87 | + if not has_topics: |
| 88 | + report_missing(file_path, "Topics") |
| 89 | + if not has_other_resources: |
| 90 | + report_missing(file_path, "Other Resources") |
| 91 | + |
| 92 | +def check_intructors(file_path): |
| 93 | + pass |
| 94 | + |
| 95 | +def check_motivation(file_path): |
| 96 | + pass |
| 97 | + |
| 98 | +def check_reference(file_path): |
| 99 | + pass |
| 100 | + |
| 101 | +def check_file(file_path): |
| 102 | + if re.search('[0-9]{2}-.*', file_path): |
| 103 | + check_lesson(file_path) |
| 104 | + elif re.search('discussion', file_path): |
| 105 | + check_discussion(file_path) |
| 106 | + elif re.search('index', file_path): |
| 107 | + check_index(file_path) |
| 108 | + elif re.search('instructors', file_path): |
| 109 | + check_intructors(file_path) |
| 110 | + elif re.search("motivation", file_path): |
| 111 | + check_motivation(file_path) |
| 112 | + elif re.search("reference", file_path): |
| 113 | + check_reference(file_path) |
| 114 | + |
| 115 | +def main(): |
| 116 | + lessons_file = os.listdir("pages") |
| 117 | + for lesson in lessons_file: |
| 118 | + if lesson.endswith('.md'): |
| 119 | + check_file('pages/{}'.format(lesson)) |
| 120 | + |
| 121 | +if __name__ == "__main__": |
| 122 | + main() |
0 commit comments