Merge pull request #117 from maxim-belkin/patch-2
authorRaniere Silva <raniere@rgaiacs.com>
Wed, 8 Feb 2017 17:41:00 +0000 (17:41 +0000)
committerGitHub <noreply@github.com>
Wed, 8 Feb 2017 17:41:00 +0000 (17:41 +0000)
Visually separate inline code blocks

14 files changed:
Makefile
_includes/carpentries.html
_includes/episode_keypoints.html
_includes/episode_navbar.html
_includes/javascript.html
_includes/lesson_footer.html
_includes/main_title.html
_includes/navbar.html
_includes/syllabus.html
_includes/workshop_ad.html
_includes/workshop_footer.html
assets/css/lesson.scss
bin/lesson_check.py
bin/lesson_initialize.py

index 0f395a310aeb0d795abed5ef50a77ee745337236..b5dfe2fa455b72c0bd029122cdc129a71cbd5d66 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -9,6 +9,7 @@ DST=_site
 
 # Controls
 .PHONY : commands clean files
+.NOTPARALLEL:
 all : commands
 
 ## commands         : show all commands.
@@ -16,11 +17,11 @@ commands :
        @grep -h -E '^##' ${MAKEFILES} | sed -e 's/## //g'
 
 ## serve            : run a local server.
-serve : lesson-rmd
+serve : lesson-md
        ${JEKYLL} serve
 
 ## site             : build files but do not run a server.
-site : lesson-rmd
+site : lesson-md
        ${JEKYLL} build
 
 # repo-check        : check repository settings.
@@ -53,7 +54,7 @@ workshop-check :
 ## ----------------------------------------
 ## Commands specific to lesson websites.
 
-.PHONY : lesson-check lesson-rmd lesson-files lesson-fixme
+.PHONY : lesson-check lesson-md lesson-files lesson-fixme
 
 # RMarkdown files
 RMD_SRC = $(wildcard _episodes_rmd/??-*.Rmd)
@@ -79,13 +80,16 @@ HTML_DST = \
   $(patsubst _extras/%.md,${DST}/%/index.html,$(wildcard _extras/*.md)) \
   ${DST}/license/index.html
 
-## lesson-rmd       : convert Rmarkdown files to markdown
-lesson-rmd: $(RMD_SRC)
-       @bin/knit_lessons.sh $(RMD_SRC)
+## lesson-md        : convert Rmarkdown files to markdown
+lesson-md : ${RMD_DST}
+
+# Use of .NOTPARALLEL makes rule execute only once
+${RMD_DST} : ${RMD_SRC}
+       @bin/knit_lessons.sh ${RMD_SRC}
 
 ## lesson-check     : validate lesson Markdown.
 lesson-check :
-       @bin/lesson_check.py -s . -p ${PARSER}
+       @bin/lesson_check.py -s . -p ${PARSER} -r _includes/links.md
 
 ## lesson-check-all : validate lesson Markdown, checking line lengths and trailing whitespace.
 lesson-check-all :
index 69e2e1cc59b859a60245e02ffb3e9f3eeafd3f4e..a0e0181fc7bf984d7b95a9c0b70e1230d6e350a4 100644 (file)
@@ -1,3 +1,6 @@
+{% comment %}
+  General description of Software and Data Carpentry.
+{% endcomment %}
 <div class="row">
   <div class="col-md-2" align="center">
     <a href="{{ site.swc_site }}"><img src="{{ page.root }}/assets/img/swc-icon-blue.svg" alt="Software Carpentry logo" /></a>
     building on learners' existing knowledge to enable them to quickly apply skills learned to their own research.
   </div>
 </div>
-
-
+<br/>
+<div class="row">
+  <div class="col-md-2" align="center">
+    <a href="{{ site.lc_site }}"><img src="{{ page.root }}/assets/img/lc-icon-black.svg" alt="Library Carpentry logo" /></a>
+  </div>
+  <div class="col-md-8">
+    Library Carpentry is made by librarians to help librarians
+    automate repetitive, boring, error-prone tasks;
+    create, maintain and analyse sustainable and reusable data;
+    work effectively with IT and systems colleagues;
+    better understand the use of software in research;
+    and much more.
+    Library Carpentry was the winner of the 2016
+    <a href="http://labs.bl.uk/British+Library+Labs+Awards">British Library Labs Teaching and Learning Award</a>.
+  </div>
+</div>
index 85378a568bca3cc554ee32a2e324e17f9382461b..2baa53ef0c4f4be23a0d291e60c44341f816be81 100644 (file)
@@ -1,3 +1,6 @@
+{% comment %}
+  Display key points for an episode.
+{% endcomment %}
 <blockquote class="keypoints">
   <h2>Key Points</h2>
   <ul>
index a789d3d9908eca4b6a61a1588259b26006c35982..1f6e033dc7825788fc159e3c5f085da20f61bef3 100644 (file)
@@ -1,26 +1,11 @@
 {% comment %}
-  Find previous and next episodes (if any).
-{% endcomment %}
-{% for episode in site.episodes  %}
-  {% if episode.url == page.url %}
-    {% unless forloop.first %}
-      {% assign prev_episode = prev %}
-    {% endunless %}
-    {% unless forloop.last %}
-      {% assign next_episode = site.episodes[forloop.index] %}
-    {% endunless %}
-  {% endif %}
-  {% assign prev = episode %}
-{% endfor %}
-
-{% comment %}
-  Display title and prev/next links.
+  Navigation bar for an episode.
 {% endcomment %}
 <div class="row">
   <div class="col-md-1">
     <h3>
-      {% if prev_episode %}
-      <a href="{{ page.root }}{{ prev_episode.url }}"><span class="glyphicon glyphicon-menu-left" aria-hidden="true"></span><span class="sr-only">previous episode</span></a>
+      {% if page.previous.url %}
+      <a href="{{ page.root }}{{ page.previous.url }}"><span class="glyphicon glyphicon-menu-left" aria-hidden="true"></span><span class="sr-only">previous episode</span></a>
       {% else %}
       <a href="{{ page.root }}/"><span class="glyphicon glyphicon-menu-up" aria-hidden="true"></span><span class="sr-only">lesson home</span></a>
       {% endif %}
@@ -34,8 +19,8 @@
   </div>
   <div class="col-md-1">
     <h3>
-      {% if next_episode %}
-      <a href="{{ page.root }}{{ next_episode.url }}"><span class="glyphicon glyphicon-menu-right" aria-hidden="true"></span><span class="sr-only">next episode</span></a>
+      {% if page.next.url %}
+      <a href="{{ page.root }}{{ page.next.url }}"><span class="glyphicon glyphicon-menu-right" aria-hidden="true"></span><span class="sr-only">next episode</span></a>
       {% else %}
       <a href="{{ page.root }}/"><span class="glyphicon glyphicon-menu-up" aria-hidden="true"></span><span class="sr-only">lesson home</span></a>
       {% endif %}
index 010ae4af13d0e3b8c1c10e36d5a91527e3e51bf0..a2066c202809b465acc364e63211461f435bbc8f 100644 (file)
@@ -1,3 +1,6 @@
+{% comment %}
+  Javascript used in lesson and workshop pages.
+{% endcomment %}
 <script src="{{ page.root }}/assets/js/jquery.min.js"></script>
 <script src="{{ page.root }}/assets/js/bootstrap.min.js"></script>
 <script src="{{ page.root }}/assets/js/lesson.js"></script>
index a37ba3eceb1bcf6b3a31fa05210219cd8bdb0366..be46c2d46d59e182b9c99d8aedfa2a4e989ccb3d 100644 (file)
@@ -1,3 +1,6 @@
+{% comment %}
+  Footer for lesson pages.
+{% endcomment %}
 <footer>
   <div class="row">
     <div class="col-md-6" align="left">
index 60b8b06937c72e5125f392ba34c7327db92de906..8e22ced110926139022b54a201d66a46c34d491d 100644 (file)
@@ -1 +1,4 @@
+{% comment %}
+  Main title for lesson pages.
+{% endcomment %}
 <h1 class="maintitle"><a href="{{ page.root }}/">{{ site.title }}</a>{% if page.title %}: {{ page.title }}{% endif %}</h1>
index 6a4df8a7c83d81f5fdec28ebf07f7511af3cd2d2..effea29dbd061db10d1b68d47a3a4ca82e45f4a9 100644 (file)
@@ -1,3 +1,6 @@
+{% comment %}
+  Lesson navigation bar.
+{% endcomment %}
 <nav class="navbar navbar-default">
   <div class="container-fluid">
     <div class="navbar-header">
index dfa7d51ec59a8fa95000bab7ba1fa926fdc38b6d..0b206cc11959b0fa3cb7c93ee866b9971b6eed9c 100644 (file)
@@ -14,6 +14,7 @@
 
   <table class="table table-striped">
   <tr>
+    <td class="col-md-1"></td>
     <td class="col-md-1"></td>
     <td class="col-md-3"><a href="{{ page.root }}/setup">Setup</a></td>
     <td class="col-md-7">Dowload files used on the lesson.</td>
index bffe9b9e25a088782f65e92408d5d26867b67cd8..e8e72e34db7bdb3f44955e76c7c13ec1fddbfdc1 100644 (file)
@@ -1,3 +1,6 @@
+{% comment %}
+  Advertising box at the top of a workshop website home page.
+{% endcomment %}
 <div class="jumbotron">
   <div class="row">
     <div class="col-md-10 col-md-offset-1">
index 31e5f37f36c83c88ac522c3d77b7e1d8894af690..3ae63e6dc8f48dd341532e0fd95582e72e50c6e5 100644 (file)
@@ -1,10 +1,16 @@
+{% comment %}
+  Footer for a standard workshop.
+{% endcomment %}
 <footer>
   <div class="row">
-    <div class="col-md-6" align="left">
+    <div class="col-md-4" align="left">
       <h4><a href="{{ site.swc_site }}">Software Carpentry</a></h4>
     </div>
-    <div class="col-md-6" align="right">
+    <div class="col-md-4" align="right">
       <h4><a href="{{ site.dc_site }}">Data Carpentry</a></h4>
     </div>
+    <div class="col-md-4" align="right">
+      <h4><a href="{{ site.lc_site }}">Library Carpentry</a></h4>
+    </div>
   </div>
 </footer>
index e067c8bcbd7bdbff1844f9d1b17da9ad4248fe78..0c5f86e1fa7c8729ee91f55e362402039d65ba55 100644 (file)
@@ -133,7 +133,7 @@ div.branding {
 
 ul,
 ol {
-  padding-left: 1em;
+  padding-left: 2em;
 }
 
 span.fold-unfold {
index a594f2f47c9a3fc0e27fe7452e399aefb9b2595f..8244222b5bd30e80516666d1dde76756c9c9300a 100755 (executable)
@@ -14,7 +14,7 @@ from optparse import OptionParser
 
 from util import Reporter, read_markdown, load_yaml, check_unwanted_files, require, IMAGE_FILE_SUFFIX
 
-__version__ = '0.2'
+__version__ = '0.3'
 
 # Where to look for source Markdown files.
 SOURCE_DIRS = ['', '_episodes', '_extras']
@@ -48,7 +48,10 @@ P_TRAILING_WHITESPACE = re.compile(r'\s+$')
 P_FIGURE_REFS = re.compile(r'<img[^>]+src="([^"]+)"[^>]*>')
 
 # Pattern to match internally-defined Markdown links.
-P_INTERNALLY_DEFINED_LINK = re.compile(r'\[[^\]]+\]\[[^\]]+\]')
+P_INTERNAL_LINK_REF = re.compile(r'\[([^\]]+)\]\[([^\]]+)\]')
+
+# Pattern to match reference links (to resolve internally-defined references).
+P_INTERNAL_LINK_DEF = re.compile(r'^\[([^\]]+)\]:\s*(.+)')
 
 # What kinds of blockquotes are allowed?
 KNOWN_BLOCKQUOTES = {
@@ -103,6 +106,8 @@ def main():
     args = parse_args()
     args.reporter = Reporter()
     check_config(args.reporter, args.source_dir)
+    args.references = read_references(args.reporter, args.reference_path)
+
     docs = read_all_markdown(args.source_dir, args.parser)
     check_fileset(args.source_dir, args.reporter, docs.keys())
     check_unwanted_files(args.source_dir, args.reporter)
@@ -110,6 +115,7 @@ def main():
         checker = create_checker(args, filename, docs[filename])
         checker.check()
     check_figures(args.source_dir, args.reporter)
+
     args.reporter.report()
 
 
@@ -126,6 +132,10 @@ def parse_args():
                       default=None,
                       dest='parser',
                       help='path to Markdown parser')
+    parser.add_option('-r', '--references',
+                      default=None,
+                      dest='reference_path',
+                      help='path to Markdown file of external references')
     parser.add_option('-s', '--source',
                       default=os.curdir,
                       dest='source_dir',
@@ -160,6 +170,37 @@ def check_config(reporter, source_dir):
                    '"root" not set to ".." in configuration')
 
 
+def read_references(reporter, ref_path):
+    """Read shared file of reference links, returning dictionary of valid references
+    {symbolic_name : URL}
+    """
+
+    result = {}
+    urls_seen = set()
+    if ref_path:
+        with open(ref_path, 'r') as reader:
+            for (num, line) in enumerate(reader):
+                line_num = num + 1
+                m = P_INTERNAL_LINK_DEF.search(line)
+                require(m,
+                        '{0}:{1} not valid reference:\n{2}'.format(ref_path, line_num, line.rstrip()))
+                name = m.group(1)
+                url = m.group(2)
+                require(name,
+                        'Empty reference at {0}:{1}'.format(ref_path, line_num))
+                reporter.check(name not in result,
+                               ref_path,
+                               'Duplicate reference {0} at line {1}',
+                               name, line_num)
+                reporter.check(url not in urls_seen,
+                               ref_path,
+                               'Duplicate definition of URL {0} at line {1}',
+                               url, line_num)
+                result[name] = url
+                urls_seen.add(url)
+    return result
+
+
 def read_all_markdown(source_dir, parser):
     """Read source files, returning
     {path : {'metadata':yaml, 'metadata_len':N, 'text':text, 'lines':[(i, line, len)], 'doc':doc}}
@@ -274,7 +315,7 @@ class CheckBase(object):
 
 
     def check(self):
-        """Run tests on metadata."""
+        """Run tests."""
 
         self.check_metadata()
         self.check_line_lengths()
@@ -342,17 +383,16 @@ class CheckBase(object):
     def check_defined_link_references(self):
         """Check that defined links resolve in the file.
 
-        Internally-defined links match the pattern [text][label].  If
-        the label contains '{{...}}', it is hopefully a references to
-        a configuration value - we should check that, but don't right
-        now.
+        Internally-defined links match the pattern [text][label].
         """
 
         result = set()
         for node in self.find_all(self.doc, {'type' : 'text'}):
-            for match in P_INTERNALLY_DEFINED_LINK.findall(node['value']):
-                if '{{' not in match:
-                    result.add(match)
+            for match in P_INTERNAL_LINK_REF.findall(node['value']):
+                text = match[0]
+                link = match[1]
+                if link not in self.args.references:
+                    result.add('"{0}"=>"{1}"'.format(text, link))
         self.reporter.check(not result,
                             self.filename,
                             'Internally-defined links may be missing definitions: {0}',
@@ -441,6 +481,14 @@ class CheckEpisode(CheckBase):
     def __init__(self, args, filename, metadata, metadata_len, text, lines, doc):
         super(CheckEpisode, self).__init__(args, filename, metadata, metadata_len, text, lines, doc)
 
+
+    def check(self):
+        """Run extra tests."""
+
+        super(CheckEpisode, self).check()
+        self.check_reference_inclusion()
+
+
     def check_metadata(self):
         super(CheckEpisode, self).check_metadata()
         if self.metadata:
@@ -467,6 +515,26 @@ class CheckEpisode(CheckBase):
                                   name, type(self.metadata[name]), type_)
 
 
+    def check_reference_inclusion(self):
+        """Check that links file has been included."""
+
+        if not self.args.reference_path:
+            return
+
+        for (i, last_line, line_len) in reversed(self.lines):
+            if last_line:
+                break
+
+        require(last_line,
+                'No non-empty lines in {0}'.format(self.filename))
+
+        include_filename = os.path.split(self.args.reference_path)[-1]
+        if include_filename not in last_line:
+            self.reporter.add(self.filename,
+                              'episode does not include "{0}"',
+                              include_filename)
+
+
 class CheckReference(CheckBase):
     """Check the reference page."""
 
index b26bf2877a5a282a4f7e05b5605ce595871ef041..540bb08c83724f964570874b1b6e796640ac99d9 100755 (executable)
@@ -158,7 +158,6 @@ You can also [reach us by email][contact].
 [dc-lessons]: http://datacarpentry.org/lessons/
 [dc-site]: http://datacarpentry.org/
 [discuss-list]: http://lists.software-carpentry.org/listinfo/discuss
-[example-site]: https://swcarpentry.github.io/lesson-example/
 [github]: http://github.com
 [github-flow]: https://guides.github.com/introduction/flow/
 [github-join]: https://github.com/join