.sass-cache
__pycache__
_site
+.Rproj.user
+.Rhistory
+.RData
+
--- /dev/null
+---
+layout: page
+title: "Contributor Code of Conduct"
+---
+As contributors and maintainers of this project,
+we pledge to follow the [Carpentry Code of Conduct][coc].
+
+Instances of abusive, harassing, or otherwise unacceptable behavior
+may be reported by following our [reporting guidelines][coc-reporting].
+
+{% include links.md %}
## Marca registrada
"Software Carpentry" y "Data Carpentry" y sus respectivos logos
-son marcas registradas de [NumFOCUS][numfocus].
+son marcas registradas de [Community Initiatives][ci].
[cc-por-humano]: https://creativecommons.org/licenses/by/4.0/
[cc-by-legal]: https://creativecommons.org/licenses/by/4.0/legalcode
[mit-license]: https://opensource.org/licenses/mit-license.html
-[numfocus]: https://numfocus.org/
+[ci]: http://communityin.org/
[osi]: https://opensource.org
# Settings
MAKEFILES=Makefile $(wildcard *.mk)
JEKYLL=jekyll
+JEKYLL_VERSION=3.7.3
PARSER=bin/markdown_ast.rb
DST=_site
commands :
@grep -h -E '^##' ${MAKEFILES} | sed -e 's/## //g'
+## docker-serve : use docker to build the site
+docker-serve :
+ docker run --rm -it -v ${PWD}:/srv/jekyll -p 127.0.0.1:4000:4000 jekyll/jekyll:${JEKYLL_VERSION} make serve
+
## serve : run a local server.
serve : lesson-md
${JEKYLL} serve
# Lesson source files in the order they appear in the navigation menu.
MARKDOWN_SRC = \
index.md \
- CONDUCT.md \
+ CODE_OF_CONDUCT.md \
setup.md \
$(sort $(wildcard _episodes/*.md)) \
reference.md \
@bin/knit_lessons.sh ${RMD_SRC}
## lesson-check : validate lesson Markdown.
-lesson-check :
+lesson-check : lesson-fixme
@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 :
- @bin/lesson_check.py -s . -p ${PARSER} -l -w
+ @bin/lesson_check.py -s . -p ${PARSER} -r _includes/links.md -l -w --permissive
## unittest : run unit tests on checking tools.
unittest :
- python bin/test_lesson_check.py
+ @bin/test_lesson_check.py
## lesson-files : show expected names of generated files for debugging.
lesson-files :
--- /dev/null
+{% assign favicon_url = site.baseurl | append: '/assets/favicons/' | append: site.carpentry | prepend: site.url %}
+
+{% if site.carpentry == 'swc' %}
+{% assign carpentry = 'Software Carpentry' %}
+{% elsif site.carpentry == 'dc' %}
+{% assign carpentry = 'Data Carpentry' %}
+{% elsif site.carpentry == 'lc' %}
+{% assign carpentry = 'Library Carpentry' %}
+{% elsif site.carpentry == 'cp' %}
+{% assign carpentry = 'The Carpentries' %}
+{% endif %}
+
+ <!-- Favicons for everyone -->
+ <link rel="apple-touch-icon-precomposed" sizes="57x57" href="{{ favicon_url }}/apple-touch-icon-57x57.png" />
+ <link rel="apple-touch-icon-precomposed" sizes="114x114" href="{{ favicon_url }}/apple-touch-icon-114x114.png" />
+ <link rel="apple-touch-icon-precomposed" sizes="72x72" href="{{ favicon_url }}/apple-touch-icon-72x72.png" />
+ <link rel="apple-touch-icon-precomposed" sizes="144x144" href="{{ favicon_url }}/apple-touch-icon-144x144.png" />
+ <link rel="apple-touch-icon-precomposed" sizes="60x60" href="{{ favicon_url }}/apple-touch-icon-60x60.png" />
+ <link rel="apple-touch-icon-precomposed" sizes="120x120" href="{{ favicon_url }}/apple-touch-icon-120x120.png" />
+ <link rel="apple-touch-icon-precomposed" sizes="76x76" href="{{ favicon_url }}/apple-touch-icon-76x76.png" />
+ <link rel="apple-touch-icon-precomposed" sizes="152x152" href="{{ favicon_url }}/apple-touch-icon-152x152.png" />
+ <link rel="icon" type="image/png" href="{{ favicon_url }}/favicon-196x196.png" sizes="196x196" />
+ <link rel="icon" type="image/png" href="{{ favicon_url }}/favicon-96x96.png" sizes="96x96" />
+ <link rel="icon" type="image/png" href="{{ favicon_url }}/favicon-32x32.png" sizes="32x32" />
+ <link rel="icon" type="image/png" href="{{ favicon_url }}/favicon-16x16.png" sizes="16x16" />
+ <link rel="icon" type="image/png" href="{{ favicon_url }}/favicon-128.png" sizes="128x128" />
+ <meta name="application-name" content="{{ carpentry }} - {{ site.title }}"/>
+ <meta name="msapplication-TileColor" content="#FFFFFF" />
+ <meta name="msapplication-TileImage" content="{{ favicon_url }}/mstile-144x144.png" />
+ <meta name="msapplication-square70x70logo" content="{{ favicon_url }}/mstile-70x70.png" />
+ <meta name="msapplication-square150x150logo" content="{{ favicon_url }}/mstile-150x150.png" />
+ <meta name="msapplication-wide310x150logo" content="{{ favicon_url }}/mstile-310x150.png" />
+ <meta name="msapplication-square310x310logo" content="{{ favicon_url }}/mstile-310x310.png" />
</ul>
<p align = "center">
<em>
- Library Carpentry te presenta los fundamentos de la informática y le proporciona una plataforma para un mayor aprendizaje autodirigido. Para obtener más información sobre lo que enseñamos y por qué consulte nuestro documento
- "<a href="http://doi.org/10.18352/lq.10176"> Library Carpentry: software skills training for library professionals</a>".
+ Library Carpentry te presenta los fundamentos de la informática y le proporciona una plataforma para un mayor aprendizaje autodirigido. Para obtener más información sobre lo que enseñamos y por qué consulte nuestro documento "<a href="http://doi.org/10.18352/lq.10176"> Library Carpentry: software skills training for library professionals</a>".
</em>
-</p>
\ No newline at end of file
+</p>
{% endcomment %}
<footer>
<div class="row">
- <div class="col-md-6" align="left">
- <h4>
- Copyright © 2016–{{ 'now' | date: "%Y" }}
+ <div class="col-md-6 copyright" align="left">
{% if site.carpentry == "swc" %}
+ Copyright © 2018–{{ 'now' | date: "%Y" }}
+ <a href="{{ site.carpentries_site }}">The Carpentries</a>
+ <br>
+ Copyright © 2016–2018
<a href="{{ site.swc_site }}">Software Carpentry Foundation</a>
{% elsif site.carpentry == "dc" %}
+ Copyright © 2018–{{ 'now' | date: "%Y" }}
+ <a href="{{ site.carpentries_site }}">The Carpentries</a>
+ <br>
+ Copyright © 2016–2018
<a href="{{ site.dc_site }}">Data Carpentry</a>
{% elsif site.carpentry == "lc" %}
+ Copyright © 2016–{{ 'now' | date: "%Y" }}
<a href="{{ site.lc_site }}">Library Carpentry</a>
+ {% elsif site.carpentry == "cp" %}
+ Copyright © 2018–{{ 'now' | date: "%Y" }}
+ <a href="{{ site.carpentries_site }}">The Carpentries</a>
{% endif %}
- </h4>
</div>
- <div class="col-md-6" align="right">
- <h4>
+ <div class="col-md-6 help-links" align="right">
{% if page.source %}
{% if page.source == "Rmd" %}
<a href="{{site.github.repository_url}}/edit/gh-pages/{{page.path|replace: "_episodes", "_episodes_rmd" | replace: ".md", ".Rmd"}}">Editar en GitHub</a>
/
<a href="{{ site.github.repository_url }}/blob/gh-pages/CITATION">Cita</a>
/
-
<a href="mailto:{{ site.email }}">Contacto</a>
- </h4>
+ </div>
+ </div>
+ <div class="row">
+ <div class="col-md-12" align="center">
+ <a href="https://github.com/carpentries/styles/">The Carpentries style</a>
+ version <a href="https://github.com/carpentries/styles/releases/tag/v9.5.2">9.5.2</a>.
</div>
</div>
</footer>
[cc-by-human]: https://creativecommons.org/licenses/by/4.0/
[cc-by-legal]: https://creativecommons.org/licenses/by/4.0/legalcode
+[ci]: http://communityin.org/
+[coc-reporting]: https://docs.carpentries.org/topic_folders/policies/code-of-conduct.html#reporting-guidelines
+[coc]: https://docs.carpentries.org/topic_folders/policies/code-of-conduct.html
[concept-maps]: https://carpentries.github.io/instructor-training/05-memory/
-[email]: mailto:lessons@software-carpentry.org
[contrib-covenant]: https://contributor-covenant.org/
[contributing]: {{ site.github.repository_url }}/blob/gh-pages/CONTRIBUTING.md
-[cran-checkpoint]: https://cran.r-project.org/web/packages/checkpoint/index.html
-[cran-knitr]: https://cran.r-project.org/web/packages/knitr/index.html
-[cran-stringr]: https://cran.r-project.org/web/packages/stringr/index.html
+[cran-checkpoint]: https://cran.r-project.org/package=checkpoint
+[cran-knitr]: https://cran.r-project.org/package=knitr
+[cran-stringr]: https://cran.r-project.org/package=stringr
+[email]: mailto:team@carpentries.org
[github-importer]: https://import.github.com/
[importer]: https://github.com/new/import
[jekyll-collection]: https://jekyllrb.com/docs/collections/
[jekyll-windows]: http://jekyll-windows.juthilo.com/
[jekyll]: https://jekyllrb.com/
[jupyter]: https://jupyter.org/
+[lesson-example]: https://carpentries.github.io/lesson-example/
[mit-license]: https://opensource.org/licenses/mit-license.html
[morea]: https://morea-framework.github.io/
[numfocus]: https://numfocus.org/
[paper-now]: https://github.com/PeerJ/paper-now
[python-gapminder]: https://swcarpentry.github.io/python-novice-gapminder/
[pyyaml]: https://pypi.python.org/pypi/PyYAML
-[r-markdown]: http://rmarkdown.rstudio.com/
+[r-markdown]: https://rmarkdown.rstudio.com/
[rstudio]: https://www.rstudio.com/
[ruby-install-guide]: https://www.ruby-lang.org/en/downloads/
[ruby-installer]: https://rubyinstaller.org/
[rubygems]: https://rubygems.org/pages/download/
-[styles]: https://github.com/swcarpentry/styles/
-[training]: https://swcarpentry.github.io/instructor-training/
+[styles]: https://github.com/carpentries/styles/
+[swc-releases]: https://github.com/swcarpentry/swc-releases
[workshop-repo]: {{ site.workshop_repo }}
[yaml]: http://yaml.org/
-[coc]: https://software-carpentry.org/conduct/
-[coc-reporting]: https://software-carpentry.org/CoC-reporting/
-[lesson-example]: https://swcarpentry.github.io/lesson-example/
<span class="icon-bar"></span>
</button>
+
{% comment %} Seleccione qué logotipo mostrar. {% endcomment %}
{% if page.carpentry == "swc" %}
<a href="{{ site.swc_site }}" class="pull-left">
<img class="navbar-logo" src="{{ page.root }}/assets/img/swc-icon-blue.svg" alt="Software Carpentry logo" />
</a>
- {% elsif page.carpentry == "dc" %}
+ {% elsif site.carpentry == "dc" %}
<a href="{{ site.dc_site }}" class="pull-left">
<img class="navbar-logo" src="{{ page.root }}/assets/img/dc-icon-black.svg" alt="Data Carpentry logo" />
</a>
- {% elsif page.carpentry == "lc" %}
+ {% elsif site.carpentry == "lc" %}
<a href="{{ site.lc_site }}" class="pull-left">
- <img class="navbar-logo" src="{{ page.root }}/assets/img/lc-icon-black.png" alt="Library Carpentry logo" />
+ <img class="navbar-logo" src="{{ page.root }}/assets/img/lc-icon-black.svg" alt="Library Carpentry logo" />
+ </a>
+ {% elsif site.carpentry == "cp" %}
+ <a href="{{ site.carpentries_site }}" class="pull-left">
+ <img class="navbar-logo" src="{{ page.root }}/assets/img/cp-logo-blue.svg" alt="The Carpentries logo" />
</a>
{% endif %}
<ul class="nav navbar-nav">
{% comment %} Mostrar siempre el código de conducta. {% endcomment %}
- <li><a href="{{ page.root }}{% link CONDUCT.md %}">Código de conducta</a></li>
+ <li><a href="{{ page.root }}{% link CODE_OF_CONDUCT.md %}">Code of Conduct</a></li>
{% if site.kind == "lesson" %}
{% comment %} Muestra las instrucciones de configuración. {% endcomment %}
<h4>
Copyright © 2016–{{ 'now' | date: "%Y" }}
{% if site.carpentry == "swc" %}
- <a href="{{ site.swc_site }}">Software Carpentry Foundation</a>
+ <a href="{{ site.swc_site }}">Software Carpentry</a>
{% elsif site.carpentry == "dc" %}
<a href="{{ site.dc_site }}">Data Carpentry</a>
{% elsif site.carpentry == "lc" %}
<a href="{{ site.lc_site }}">Library Carpentry</a>
+ {% elsif site.carpentry == "cp" %}
+ <a href="{{ site.carpentries_site }}">The Carpentries</a>
{% endif %}
</h4>
</div>
<link rel="stylesheet" type="text/css" href="{{ page.root }}/assets/css/bootstrap-theme.css" />
<link rel="stylesheet" type="text/css" href="{{ page.root }}/assets/css/lesson.css" />
<link rel="stylesheet" type="text/css" href="{{ page.root }}/assets/css/syntax.css" />
- {% if site.carpentry == "swc" %}
- <link rel="shortcut icon" type="image/x-icon" href="/favicon-swc.ico" />
- {% elsif site.carpentry == "dc" %}
- <link rel="shortcut icon" type="image/x-icon" href="/favicon-dc.ico" />
- {% elsif site.carpentry == "lc" %}
- <link rel="shortcut icon" type="image/x-icon" href="/favicon-lc.ico" />
- {% endif %}
+
+ {% include favicons.html %}
+
<!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
<link rel="stylesheet" type="text/css" href="{{ page.root }}/assets/css/bootstrap.css" />
<link rel="stylesheet" type="text/css" href="{{ page.root }}/assets/css/bootstrap-theme.css" />
<link rel="stylesheet" type="text/css" href="{{ page.root }}/assets/css/lesson.css" />
- {% if site.carpentry == "swc" %}
- <link rel="shortcut icon" type="image/x-icon" href="/favicon-swc.ico" />
- {% elsif site.carpentry == "dc" %}
- <link rel="shortcut icon" type="image/x-icon" href="/favicon-dc.ico" />
- {% elsif site.carpentry == "lc" %}
- <link rel="shortcut icon" type="image/x-icon" href="/favicon-lc.ico" />
- {% endif %}
+
+ {% include favicons.html %}
+
<!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
samp {
font-family: monospace, monospace;
font-size: 1em;
+ hyphens: none;
}
button,
input,
.solution{ @include bkSetup($color-solution, "\e105"); }
.testimonial{ @include bkSetup($color-testimonial, "\e143"); }
+.callout h3,
+.challenge h3,
+.checklist h3,
+.discussion h3,
+.keypoints h3,
+.objectives h3,
+.prereq h3,
+.solution h3,
+.testimonial h3 {
+font-size: 18px;
+}
+
//----------------------------------------
// Override Bootstrap settings.
//----------------------------------------
code {
+ white-space: nowrap;
padding: 2px 5px;
color: #3d90d9;
background-color: #e7e7e7;
text-align: center;
}
+footer .copyright,
+footer .help-links
+{
+ font-size: 18px;
+ margin-top: 10px;
+ margin-bottom: 10px;
+ font-weight: 500;
+ line-height: 1.1;
+}
+
img.navbar-logo {
height: 40px; // synchronize with height of navbar
padding-top: 5px;
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<svg viewBox="23.011598587036133 176.38101196289062 76.86479759216309 79.23809814453125" width="76.86479759216309" height="79.23809814453125" xmlns="http://www.w3.org/2000/svg">
+ <path id="path2" style="fill:#071159;fill-rule:evenodd" d="m 60.4089,220.1395 -0.609,-15.6219 13.481,-8.4941 13.6126,7.2951 0.692,16.0431 -13.481,8.4935 z" transform="matrix(1, 0, 0, 1, 3.552713678800501e-15, 0)"/>
+ <path id="path4" style="fill:#071159;fill-rule:evenodd" d="m 33.6628,217.112 -0.0823,-22.4502 29.2906,-18.2808 14.0091,8.3629 c -10.1028,6.5645 -14.7455,9.3309 -24.9875,15.6991 l -0.1926,5.9751 z" transform="matrix(1, 0, 0, 1, 3.552713678800501e-15, 0)"/>
+ <path id="path6" style="fill:#071159;fill-rule:evenodd" d="m 35.0548,248.4071 -12.0432,-19.0814 28.7863,-16.8447 1.1557,12.3346 10.7928,5.589 z" transform="matrix(1, 0, 0, 1, 3.552713678800501e-15, 0)"/>
+ <path id="path8" style="fill:#071159;fill-rule:evenodd" d="m 50.128,245.2642 19.919,10.3549 29.8294,-18.2487 -1.8935,-15.6419 c -10.3375,6.1835 -14.3477,8.7856 -24.6876,14.9664 l -5.3995,-2.5663 z" transform="matrix(1, 0, 0, 1, 3.552713678800501e-15, 0)"/>
+</svg>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ version="1.1"
+ id="Layer_1_2_"
+ width="177.26501"
+ height="59.007732"
+ inkscape:version="0.91 r13725"
+ sodipodi:docname="dc.svg">
+ <metadata
+ id="metadata3608">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs3606" />
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1088"
+ inkscape:window-height="692"
+ id="namedview3604"
+ showgrid="false"
+ inkscape:zoom="1.2228904"
+ inkscape:cx="53.730618"
+ inkscape:cy="118.68094"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="Layer_1_2_"
+ fit-margin-top="0"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#ffffff;stroke:none"
+ d="m 377.18496,-71.886104 0,2 45,0 c 16.6071,0 33.5691,0.30551 49,7.312499 9.35004,4.2457 17.36004,10.6668 22.47504,19.687501 12.397,21.8653 4.995,51.7467998 -16.475,64.5471 -11.73124,6.9939 -24.67714,8.6249 -38.00004,9.5429 -20.5979,1.418 -41.3244,0.91 -62,0.91 l 0,47.000004 c 3.33725,-3.967 5.80744,-8.339 10.0008,-11.606 8.2497,-6.428 20.1709,-6.498 29.9992,-5.343 4.7185,0.554 19.3015,3.864 17.2485,10.953 -0.8017,2.768 -4.2276,6.772 -7.2524,7.035 -2.7517,0.238 -5.4904,-2.322 -7.9961,-3.189 -8.0047,-2.769 -23.6558,-3.279 -26.4128,7.154 -1.8285,6.919 3.1413,11.863 9.4128,13.315 9.0819,2.103 19.2695,0.393 25,-7.319 2.5834,2.638 9.0984,7.685 7.8079,11.96 -1.7806,5.898 -11.6335,8.206 -16.8079,9.116 -14.7799,2.601 -35.06049,-0.446 -40,-17.076 -1.98057,4.72 -1,10.92 -1,16 l 0,32 31,0 0,74 -31,0 0,162 485.00004,0 0,-236 89,0 c 14.402,0 29.704,-1.469 44,0.286 23.229,2.853 46.807,14.301 62.575,31.753 15.726,17.408 26.01,38.469 28.255,61.961 0.723,7.564 0.17,15.404 0.17,23 0,13.504 0.423,26.787 -2.895,40 -8.066,32.114 -31.7,58.372 -62.105,70.988 -10.518,4.365 -21.596,7.004 -33,7.012 3.699,1.552 8.017,1 12,1 l 20,0 74,0 0,-450.000004 -78,0 c 14.746,26.487099 34.719,50.8526 51.67,75.9999998 6.071,9.0075002 14.11,17.9675002 18.33,28.0000002 l -23,0 c -3.419,0 -7.788,0.668 -10.907,-1.028 -6.115,-3.3261 -7.365,-13.6847 -14.277,-15.6541 -5.282,-1.5047 -12.346,-0.3179 -17.816,-0.3179 -11.293,0 -22.704,-0.4819 -33.985,0.0193 -9.728,0.4322 -9.363,13.0897 -17.199,16.3777 -3.129,1.312 -7.496,0.603 -10.816,0.603 l -22,0 c 3.153,-8.1975 9.791,-15.6861 14.667,-23.0000002 10.665,-15.9976 21.04,-32.2397998 32.053,-47.9999998 7.161,-10.248501 16.837,-21.276901 21.28,-33 l -358,0 c 4.985,13.153199 16.243,25.497499 24.28,37 15.481,22.1552 30.605,44.5937998 45.72,67 l -24,0 c -3.359,0 -7.775,0.7 -10.816,-1.028 -7.209,-4.0961 -7.694,-15.5368 -17.188,-15.9527 -10.954,-0.4799 -22.031,-0.0193 -32.996,-0.0193 -5.585,0 -12.539,-1.1105 -17.96,0.3179 -7.241,1.9083 -8.377,12.0616 -14.366,15.6541 -2.937,1.763 -7.397,1.028 -10.674,1.028 l -23,0 c 4.568,-9.6555 11.751,-18.1422 17.667,-27.0000002 l 34,-51.0000008 c 5.767,-8.6466 12.272,-16.90872 17.333,-25.999999 l -257.00004,0 z"
+ id="path3588" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#231f20;stroke:none"
+ d="m 0,0.00761578 0,18.50000022 7.75,0 0,-18.50000022 -7.75,0 m 15.25,0 0,23.50000022 7.75,0 0,-23.50000022 -7.75,0 m 15,0 0,28.50000022 7.75,0 0,-28.50000022 -7.75,0 m 15.25,0 0,33.75000022 7.75,0 0,-33.75000022 -7.75,0 m 15,0 0,38.75000022 7.75,0 0,-38.75000022 -7.75,0 m 15.25,0 0,43.75000022 7.75,0 0,-43.75000022 -7.75,0 m 15.25,0 0,48.75000422 7.75,0 0,-48.75000422 -7.75,0 m 15,0 0,53.75000422 7.75,0 0,-53.75000422 -7.75,0 m 15.25,0 0,59.00000422 22.75,0 c 7.48325,0 14.64825,0.0795 21.25,-4.0165 5.92275,-3.67475 10.22625,-9.880254 11.54875,-16.733504 0.56,-2.901 0.45125,-5.81 0.45125,-8.75 0,-3.015 0.14275,-6.027 -0.452,-9 C 175.29725,13.004866 170.30275,6.3398658 163.5,2.8221158 157.48325,-0.28913422 151.318,0.00761578 144.75,0.00761578 l -23.5,0 z"
+ id="path3598" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#ffffff;stroke:none"
+ d="m 129,7.7576158 0,43.2500042 17.25,0 c 4.75575,0 9.147,0.227 13.5,-2.02725 4.562,-2.36225 8.00875,-6.703504 9.191,-11.722754 0.6255,-2.656 0.559,-5.29175 0.559,-8 0,-7.52075 -1.64,-14.469 -8.5,-18.7335 -4.72075,-2.9347502 -9.6605,-2.7665002 -15,-2.7665002 l -17,0 z"
+ id="path3600" />
+ <path
+ inkscape:connector-curvature="0"
+ style="fill:#231f20;stroke:none"
+ d="m 136.5,15.257616 0,28.5 10.5,0 c 2.19,0 4.59025,0.27325 6.75,-0.11625 3.7395,-0.674 6.79175,-3.578 7.9775,-7.13375 0.408,-1.224 0.519,-2.46925 0.5225,-3.75 l -7.75,0 c -1.08575,4.51625 -6.67475,3.25 -10.25,3.25 l 0,-13 c 2.356,0 5.49425,-0.54525 7.75,0.16325 1.51175,0.47475 1.6535,2.1015 2.8545,2.686 0.66525,0.3235 1.6775,0.15075 2.3955,0.15075 l 5,0 c -0.0405,-4.8855 -3.1365,-9.344 -8,-10.525 -1.98675,-0.4825 -4.21775,-0.225 -6.25,-0.225 l -11.5,0 z"
+ id="path3602" />
+</svg>
version="1.1"
id="svg2"
xml:space="preserve"
- width="1056"
- height="816"
- viewBox="0 0 1056 816"
+ width="885.22382"
+ height="544.16864"
+ viewBox="0 0 885.22382 544.16864"
sodipodi:docname="lc-icon-black.svg"
- inkscape:version="0.92.1 r"><metadata
+ inkscape:version="0.92.3 (2405546, 2018-03-11)"><metadata
id="metadata8"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
- inkscape:window-width="923"
- inkscape:window-height="480"
+ inkscape:window-width="3840"
+ inkscape:window-height="2031"
id="namedview4"
showgrid="false"
- inkscape:zoom="0.28921569"
- inkscape:cx="528"
- inkscape:cy="408"
+ inkscape:zoom="1.1568627"
+ inkscape:cx="1222.0865"
+ inkscape:cy="406.12625"
inkscape:window-x="0"
- inkscape:window-y="0"
- inkscape:window-maximized="0"
- inkscape:current-layer="g10" /><g
+ inkscape:window-y="55"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="g10"
+ fit-margin-top="10"
+ fit-margin-left="10"
+ fit-margin-right="10"
+ fit-margin-bottom="10" /><g
id="g10"
inkscape:groupmode="layer"
inkscape:label="LibraryCarpentryLogo_Draft_04"
- transform="matrix(1.3333333,0,0,-1.3333333,0,816)"><g
+ transform="matrix(1.3333333,0,0,-1.3333333,-85.388398,680.08465)"><g
id="g12"><g
id="g14"
clip-path="url(#clipPath18)"><g
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ version="1.1"
+ id="svg2"
+ xml:space="preserve"
+ width="1056"
+ height="816"
+ viewBox="0 0 1056 816"
+ sodipodi:docname="lc-icon-black.svg"
+ inkscape:version="0.92.1 r"><metadata
+ id="metadata8"><rdf:RDF><cc:Work
+ rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
+ id="defs6"><clipPath
+ clipPathUnits="userSpaceOnUse"
+ id="clipPath18"><path
+ d="M 0,612 H 792 V 0 H 0 Z"
+ id="path16"
+ inkscape:connector-curvature="0" /></clipPath></defs><sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="923"
+ inkscape:window-height="480"
+ id="namedview4"
+ showgrid="false"
+ inkscape:zoom="0.28921569"
+ inkscape:cx="528"
+ inkscape:cy="408"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="g10" /><g
+ id="g10"
+ inkscape:groupmode="layer"
+ inkscape:label="LibraryCarpentryLogo_Draft_04"
+ transform="matrix(1.3333333,0,0,-1.3333333,0,816)"><g
+ id="g12"><g
+ id="g14"
+ clip-path="url(#clipPath18)"><g
+ id="g20"
+ transform="translate(314.9707,239.4268)"><path
+ d="M 0,0 C 8.395,0 13.455,3.795 18.055,7.935 V 21.506 C 6.095,21.391 -9.775,21.391 -9.775,9.431 -9.775,3.335 -6.441,0 0,0 m 18.055,29.786 v 5.405 c 0,5.175 -3.105,9.662 -12.651,9.662 -10.465,0 -16.791,-4.832 -17.94,-4.832 -1.61,0 -4.025,4.486 -4.025,5.751 0,2.991 10.12,7.36 21.621,7.36 14.26,0 22.656,-6.67 22.656,-18.055 V 9.315 c 0,-9.43 1.38,-13.916 1.38,-14.49 0,-2.185 -4.715,-2.646 -6.21,-2.646 -2.761,0 -3.335,4.831 -4.026,8.05 -4.37,-4.83 -10.695,-8.625 -19.321,-8.625 -11.155,0 -19.666,5.406 -19.666,17.251 0,19.436 20.586,20.701 38.182,20.931"
+ style="fill:#7b7979;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ id="path22"
+ inkscape:connector-curvature="0" /></g><g
+ id="g24"
+ transform="translate(362.2344,274.3887)"><path
+ d="m 0,0 c 0,9.43 -1.381,13.915 -1.381,14.49 0,2.186 4.716,2.645 6.211,2.645 2.76,0 3.336,-4.6 4.025,-7.59 3.911,5.52 8.51,8.625 13.34,8.625 4.487,0 6.671,-1.955 6.671,-4.484 0,-2.186 -0.92,-6.211 -2.3,-6.211 C 25.071,7.475 24.15,8.74 20.24,8.74 14.261,8.74 9.66,1.725 9.66,1.725 v -42.092 c 0,-1.611 -1.38,-2.53 -4.599,-2.53 H 4.6 c -3.22,0 -4.6,0.919 -4.6,2.53 z"
+ style="fill:#7b7979;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ id="path26"
+ inkscape:connector-curvature="0" /></g><g
+ id="g28"
+ transform="translate(427.4404,239.541)"><path
+ d="m 0,0 h 0.69 c 9.89,0.23 15.065,7.706 15.065,21.392 0,14.835 -5.06,23.231 -14.145,23.231 -5.06,0 -10.81,-2.761 -15.526,-7.935 V 5.176 C -9.776,1.726 -4.945,0 0,0 m -13.916,-29.211 c 0,-1.609 -1.38,-2.53 -4.601,-2.53 h -0.459 c -3.22,0 -4.6,0.921 -4.6,2.53 v 64.059 c 0,9.429 -1.38,13.915 -1.38,14.49 0,2.185 4.715,2.644 6.21,2.644 2.761,0 3.335,-4.599 4.025,-7.589 5.636,5.865 11.731,8.625 18.286,8.625 13.801,0 22.311,-12.88 22.311,-30.362 v -1.035 c 0,-19.32 -9.66,-30.131 -23.346,-30.131 -7.131,0 -11.961,2.415 -16.446,5.865 z"
+ style="fill:#7b7979;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ id="path30"
+ inkscape:connector-curvature="0" /></g><g
+ id="g32"
+ transform="translate(506.4453,266.1074)"><path
+ d="M 0,0 C 0,12.076 -4.6,18.172 -13.801,18.172 -23.806,18.172 -29.096,12.076 -29.9,0 Z m -13.455,26.451 c 15.756,0 23.691,-10.465 23.691,-30.015 0,-1.381 -2.416,-4.715 -3.795,-4.715 H -29.9 c 0.92,-12.766 6.324,-18.517 17.711,-18.517 10.004,0 15.64,4.83 16.79,4.83 1.61,0 4.025,-4.485 4.025,-5.75 0,-2.761 -9.085,-7.36 -20.701,-7.36 -19.091,0 -28.177,9.775 -28.177,30.361 0,20.356 9.201,31.166 26.797,31.166"
+ style="fill:#7b7979;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ id="path34"
+ inkscape:connector-curvature="0" /></g><g
+ id="g36"
+ transform="translate(531.7422,274.3887)"><path
+ d="m 0,0 c 0,9.43 -1.38,13.915 -1.38,14.49 0,2.186 4.716,2.645 6.21,2.645 2.761,0 3.336,-4.6 4.025,-7.59 4.831,5.405 11.501,8.625 17.942,8.625 11.73,0 18.746,-7.015 18.746,-18.631 v -39.906 c 0,-1.611 -1.381,-2.53 -4.601,-2.53 h -0.46 c -3.22,0 -4.6,0.919 -4.6,2.53 v 38.986 c 0,7.016 -4.255,11.156 -11.731,11.156 -5.174,0 -10.695,-3.105 -14.491,-8.05 v -42.092 c 0,-1.611 -1.379,-2.53 -4.599,-2.53 h -0.46 c -3.22,0 -4.601,0.919 -4.601,2.53 z"
+ style="fill:#7b7979;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ id="path38"
+ inkscape:connector-curvature="0" /></g><g
+ id="g40"
+ transform="translate(598.2119,283.5889)"><path
+ d="M 0,0 H -5.751 C -7.36,0 -8.05,1.034 -8.05,3.45 v 1.38 c 0,2.415 0.69,3.45 2.299,3.45 L 0,8.28 1.38,20.356 c 0.115,1.61 1.266,2.53 3.68,2.53 h 1.15 c 2.416,0 3.45,-0.92 3.45,-2.53 V 8.28 h 9.661 c 1.61,0 2.3,-1.035 2.3,-3.45 V 3.45 C 21.621,1.034 20.931,0 19.321,0 H 9.66 v -35.767 c 0,-6.9 2.876,-8.51 6.21,-8.51 3.336,0 3.91,0.575 4.945,0.575 1.611,0 2.416,-4.486 2.416,-5.405 0,-3.106 -5.291,-3.451 -8.396,-3.451 C 9.085,-52.558 0,-50.027 0,-36.687 Z"
+ style="fill:#7b7979;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ id="path42"
+ inkscape:connector-curvature="0" /></g><g
+ id="g44"
+ transform="translate(635.5859,274.3887)"><path
+ d="m 0,0 c 0,9.43 -1.38,13.915 -1.38,14.49 0,2.186 4.716,2.645 6.21,2.645 2.761,0 3.336,-4.6 4.025,-7.59 3.911,5.52 8.511,8.625 13.341,8.625 4.486,0 6.671,-1.955 6.671,-4.484 0,-2.186 -0.92,-6.211 -2.301,-6.211 -1.494,0 -2.415,1.265 -6.325,1.265 C 14.262,8.74 9.66,1.725 9.66,1.725 v -42.092 c 0,-1.611 -1.379,-2.53 -4.599,-2.53 h -0.46 c -3.22,0 -4.601,0.919 -4.601,2.53 z"
+ style="fill:#7b7979;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ id="path46"
+ inkscape:connector-curvature="0" /></g><g
+ id="g48"
+ transform="translate(669.7402,288.6484)"><path
+ d="m 0,0 c -0.114,0.346 -0.344,1.035 -0.344,1.496 0,1.15 1.494,1.955 4.945,1.955 h 0.92 c 2.299,0 4.025,-0.461 4.6,-1.841 L 25.762,-39.906 41.057,1.61 c 0.461,1.38 2.3,1.841 4.716,1.841 3.45,0 4.946,-0.69 4.946,-1.955 0,-0.461 -0.115,-0.92 -0.346,-1.611 L 20.816,-78.434 c -0.689,-1.724 -2.644,-2.415 -5.404,-2.415 h -1.726 c -2.53,0 -3.795,0.691 -3.795,1.841 0,0.69 0.23,0.92 10.236,25.301 z"
+ style="fill:#7b7979;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ id="path50"
+ inkscape:connector-curvature="0" /></g><g
+ id="g52"
+ transform="translate(173.6738,440.5723)"><path
+ d="m 0,0 c 0,1.609 1.381,2.529 4.602,2.529 h 0.459 c 3.22,0 4.6,-0.92 4.6,-2.529 v -55.548 c 0,-1.61 -1.38,-2.53 -4.6,-2.53 H 4.602 c -3.221,0 -4.602,0.92 -4.602,2.53 z m -0.459,19.32 v 6.785 c 0,1.611 1.38,2.531 4.6,2.531 h 1.38 c 3.22,0 4.6,-0.92 4.6,-2.531 V 19.32 c 0,-1.609 -1.38,-2.529 -4.6,-2.529 h -1.38 c -3.22,0 -4.6,0.92 -4.6,2.529"
+ style="fill:#7b7979;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ id="path54"
+ inkscape:connector-curvature="0" /></g><g
+ id="g56"
+ transform="translate(228.6465,390.4297)"><path
+ d="m 0,0 c 9.084,0 14.145,8.395 14.145,23.23 0,13.686 -5.175,21.163 -15.066,21.392 h -0.69 c -4.946,0 -9.776,-1.726 -13.915,-5.175 V 7.936 C -10.811,2.76 -5.061,0 0,0 m -25.188,76.248 c 0,1.61 1.381,2.53 4.601,2.53 h 0.46 c 3.221,0 4.601,-0.92 4.601,-2.53 v -28.98 c 4.485,3.449 9.315,5.865 16.446,5.865 13.685,0 23.346,-10.811 23.346,-30.132 v -1.035 c 0,-17.482 -8.511,-30.362 -22.311,-30.362 -6.557,0 -12.651,2.761 -18.287,8.626 -0.805,-4.025 -1.84,-6.786 -1.84,-6.786 -0.46,-1.034 -1.494,-1.38 -3.105,-1.38 h -0.459 c -2.416,0 -3.452,0.92 -3.452,2.531 z"
+ style="fill:#7b7979;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ id="path58"
+ inkscape:connector-curvature="0" /></g><g
+ id="g60"
+ transform="translate(269.4678,425.3906)"><path
+ d="m 0,0 c 0,9.432 -1.38,13.916 -1.38,14.491 0,2.185 4.715,2.646 6.21,2.646 2.761,0 3.335,-4.601 4.025,-7.591 3.911,5.52 8.511,8.626 13.341,8.626 4.485,0 6.67,-1.955 6.67,-4.486 0,-2.185 -0.92,-6.209 -2.3,-6.209 -1.495,0 -2.415,1.263 -6.325,1.263 C 14.261,8.74 9.66,1.726 9.66,1.726 v -42.092 c 0,-1.611 -1.38,-2.53 -4.6,-2.53 H 4.601 C 1.38,-42.896 0,-41.977 0,-40.366 Z"
+ style="fill:#7b7979;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ id="path62"
+ inkscape:connector-curvature="0" /></g><g
+ id="g64"
+ transform="translate(326.9688,390.4297)"><path
+ d="m 0,0 c 8.395,0 13.455,3.795 18.055,7.936 v 13.57 C 6.095,21.391 -9.775,21.391 -9.775,9.43 -9.775,3.335 -6.441,0 0,0 m 18.055,29.786 v 5.405 c 0,5.176 -3.105,9.661 -12.651,9.661 -10.465,0 -16.791,-4.831 -17.94,-4.831 -1.61,0 -4.025,4.486 -4.025,5.75 0,2.991 10.12,7.362 21.621,7.362 14.26,0 22.656,-6.671 22.656,-18.057 V 9.315 c 0,-9.43 1.38,-13.916 1.38,-14.491 0,-2.184 -4.715,-2.644 -6.21,-2.644 -2.761,0 -3.335,4.83 -4.026,8.05 -4.37,-4.831 -10.695,-8.626 -19.321,-8.626 -11.155,0 -19.666,5.406 -19.666,17.251 0,19.436 20.586,20.702 38.182,20.931"
+ style="fill:#7b7979;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ id="path66"
+ inkscape:connector-curvature="0" /></g><g
+ id="g68"
+ transform="translate(374.2324,425.3906)"><path
+ d="m 0,0 c 0,9.432 -1.381,13.916 -1.381,14.491 0,2.185 4.716,2.646 6.211,2.646 2.76,0 3.336,-4.601 4.025,-7.591 3.911,5.52 8.51,8.626 13.34,8.626 4.487,0 6.671,-1.955 6.671,-4.486 0,-2.185 -0.92,-6.209 -2.3,-6.209 C 25.071,7.477 24.15,8.74 20.24,8.74 14.261,8.74 9.66,1.726 9.66,1.726 v -42.092 c 0,-1.611 -1.38,-2.53 -4.599,-2.53 H 4.6 c -3.22,0 -4.6,0.919 -4.6,2.53 z"
+ style="fill:#7b7979;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ id="path70"
+ inkscape:connector-curvature="0" /></g><g
+ id="g72"
+ transform="translate(408.3867,439.6523)"><path
+ d="m 0,0 c -0.115,0.345 -0.345,1.035 -0.345,1.494 0,1.151 1.495,1.955 4.945,1.955 h 0.921 c 2.299,0 4.024,-0.459 4.6,-1.84 L 25.762,-39.907 41.057,1.609 c 0.461,1.381 2.3,1.84 4.715,1.84 3.451,0 4.945,-0.689 4.945,-1.955 0,-0.459 -0.114,-0.92 -0.344,-1.609 L 20.816,-78.434 c -0.69,-1.725 -2.645,-2.416 -5.406,-2.416 h -1.724 c -2.53,0 -3.795,0.691 -3.795,1.84 0,0.691 0.23,0.92 10.235,25.302 z"
+ style="fill:#7b7979;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ id="path74"
+ inkscape:connector-curvature="0" /></g><g
+ id="g76"
+ transform="translate(268.1396,298.084)"><path
+ d="m 0,0 h -46.771 v -23.824 l 20.206,0.731 c 0,0 18.242,-2.564 26.565,23.093"
+ style="fill:#7b7979;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ id="path78"
+ inkscape:connector-curvature="0" /></g><g
+ id="g80"
+ transform="translate(186.2686,298.084)"><path
+ d="m 0,0 h -42.728 v -25.971 c 9.594,-3.48 13.341,-10.605 25.592,-10.605 9.515,0 13.448,2.511 17.136,5.279 z"
+ style="fill:#7b7979;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ id="path82"
+ inkscape:connector-curvature="0" /></g><g
+ id="g84"
+ transform="translate(209.8252,213.8242)"><path
+ d="m 0,0 c 0,-4.353 -2.758,-7.882 -6.156,-7.882 -3.4,0 -6.157,3.529 -6.157,7.882 v 88.789 c 0,4.357 2.757,7.885 6.157,7.885 3.398,0 6.156,-3.528 6.156,-7.885 z m 97.275,115.246 h -2.406 v 5.904 c 0.123,0.248 0.2,0.528 0.2,0.827 v 24.638 c 0,1 -0.78,1.811 -1.744,1.811 H 20.678 c -0.499,0.054 -0.99,-0.082 -1.342,-0.432 l -39.469,-39.023 c -0.385,-0.381 -0.516,-0.926 -0.424,-1.473 V -14.907 c 0,-0.786 0.368,-1.451 0.879,-1.705 l 37.85,-37.372 c 0.118,-0.117 0.256,-0.2 0.402,-0.267 0.25,-0.142 0.533,-0.23 0.84,-0.23 h 54.664 v -1.984 c 0,-1.01 0.798,-1.828 1.783,-1.828 h 16.87 c 0.986,0 1.784,0.818 1.784,1.828 v 32.827 c 0,1.011 -0.798,1.828 -1.784,1.828 h -16.87 c -0.985,0 -1.783,-0.817 -1.783,-1.828 v -2.586 H 30.566 L 8.307,-4.243 V 97.994 l 22.425,22.172 h 35.677 v -4.92 h -2.608 c -1.039,0 -1.881,-0.779 -1.881,-1.744 v -5.291 c 0,-0.965 0.842,-1.744 1.881,-1.744 h 33.474 c 1.038,0 1.883,0.779 1.883,1.744 v 5.291 c 0,0.965 -0.845,1.744 -1.883,1.744"
+ style="fill:#7b7979;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ id="path86"
+ inkscape:connector-curvature="0" /></g><g
+ id="g88"
+ transform="translate(130.4912,285.0215)"><path
+ d="m 0,0 c 0,-3.017 -2.265,-5.463 -5.058,-5.463 -2.793,0 -5.056,2.446 -5.056,5.463 0,3.016 2.263,5.461 5.056,5.461 C -2.265,5.461 0,3.016 0,0 m 10.128,209.072 v 8.47 H -20.243 V -25.936 h 30.371 v 64.17 H 2.394 v 5.678 h 7.734 V 61.827 H -2.553 v 5.679 H 10.128 V 85.421 H 2.394 v 5.681 h 7.734 v 17.914 H -2.553 v 5.679 h 12.681 v 17.914 H 2.394 v 5.68 h 7.734 v 17.914 H -2.553 v 5.68 h 12.681 v 17.914 H 2.394 v 5.681 h 7.734 v 17.916 H -2.553 v 5.678 z"
+ style="fill:#7b7979;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ id="path90"
+ inkscape:connector-curvature="0" /></g><g
+ id="g92"
+ transform="translate(107.7373,298.084)"><path
+ d="m 0,0 h -36.196 c 9.542,-26.602 27.375,-23.093 27.375,-23.093 L 0,-23.428 Z"
+ style="fill:#7b7979;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ id="path94"
+ inkscape:connector-curvature="0" /></g><g
+ id="g96"
+ transform="translate(331.3496,127.376)"><path
+ d="m 0,0 h -16.679 c -0.957,0 -1.731,-0.771 -1.731,-1.726 V -2.75 h -11.653 v 5.289 c 0,1.065 -0.859,1.927 -1.918,1.927 h -1.387 v 20.79 h -8.378 V 4.466 h -1.386 c -1.06,0 -1.918,-0.862 -1.918,-1.927 V -2.75 h -37.023 l 0.01,0.706 c 0,0.69 -0.654,2.1 -1.276,2.1 h -0.994 v 0.019 l -6.413,-0.001 v -13.827 l 3.643,10e-4 c 0.114,-0.043 0.231,-0.08 0.358,-0.08 h 3.406 c 0.622,0 1.123,0.56 1.123,1.25 l 0.035,2.363 h 37.131 v -5.794 c 0,-1.064 0.858,-1.926 1.918,-1.926 h 11.151 c 1.059,0 1.918,0.862 1.918,1.926 v 5.794 h 11.653 v -1.529 c 0,-0.954 0.774,-1.726 1.731,-1.726 H 0 c 0.956,0 1.731,0.772 1.731,1.726 V -1.726 C 1.731,-0.771 0.956,0 0,0"
+ style="fill:#7b7979;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ id="path98"
+ inkscape:connector-curvature="0" /></g><g
+ id="g100"
+ transform="translate(285.8154,217.1611)"><path
+ d="m 0,0 h 3.998 v -21.466 h 8.919 V 0 h 3.999 c 1.023,0 1.855,0.781 1.855,1.745 v 5.292 c 0,0.088 -0.014,0.174 -0.027,0.258 V 9.589 H -1.828 V 7.295 C -1.842,7.211 -1.855,7.125 -1.855,7.037 V 1.745 C -1.855,0.781 -1.025,0 0,0"
+ style="fill:#7b7979;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ id="path102"
+ inkscape:connector-curvature="0" /></g></g></g></g></svg>
\ No newline at end of file
--- /dev/null
+# dist: trusty # Ubuntu 14.04
+language: python
+python: 3.6
+branches:
+ only:
+ - gh-pages
+ - /.*/
+before_install:
+ - sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys E084DAB9
+ - echo "deb https://cran.rstudio.com/bin/linux/ubuntu trusty/" | sudo tee -a /etc/apt/sources.list
+ - sudo apt-get update -y
+ - sudo apt-get install -y r-base
+ - sudo Rscript -e "install.packages('knitr', repos = 'https://', dependencies = TRUE)"
+ - sudo Rscript -e "install.packages('stringr', repos = 'https://cran.rstudio.com', dependencies = TRUE)"
+ - sudo Rscript -e "install.packages('checkpoint', repos = 'https://cran.rstudio.com', dependencies = TRUE)"
+ - sudo Rscript -e "install.packages('ggplot2', repos = 'https://cran.rstudio.com', dependencies = TRUE)"
+ - rvm default
+ - gem install json kramdown jekyll
+install:
+ - pip install pyyaml
+script:
+ - make lesson-check-all
+ - make --always-make site
--- /dev/null
+FIXME: list authors' names and email addresses.
\ No newline at end of file
--- /dev/null
+FIXME: describe how to cite this lesson.
\ No newline at end of file
--- /dev/null
+---
+layout: page
+title: "Contributor Code of Conduct"
+---
+As contributors and maintainers of this project,
+we pledge to follow the [Carpentry Code of Conduct][coc].
+
+Instances of abusive, harassing, or otherwise unacceptable behavior
+may be reported by following our [reporting guidelines][coc-reporting].
+
+{% include links.md %}
--- /dev/null
+# Contributing
+
+[Software Carpentry][swc-site] and [Data Carpentry][dc-site] are open source projects,
+and we welcome contributions of all kinds:
+new lessons,
+fixes to existing material,
+bug reports,
+and reviews of proposed changes are all welcome.
+
+## Contributor Agreement
+
+By contributing,
+you agree that we may redistribute your work under [our license](LICENSE.md).
+In exchange,
+we will address your issues and/or assess your change proposal as promptly as we can,
+and help you become a member of our community.
+Everyone involved in [Software Carpentry][swc-site] and [Data Carpentry][dc-site]
+agrees to abide by our [code of conduct](CODE_OF_CONDUCT.md).
+
+## How to Contribute
+
+The easiest way to get started is to file an issue
+to tell us about a spelling mistake,
+some awkward wording,
+or a factual error.
+This is a good way to introduce yourself
+and to meet some of our community members.
+
+1. If you do not have a [GitHub][github] account,
+ you can [send us comments by email][email].
+ However,
+ we will be able to respond more quickly if you use one of the other methods described below.
+
+2. If you have a [GitHub][github] account,
+ or are willing to [create one][github-join],
+ but do not know how to use Git,
+ you can report problems or suggest improvements by [creating an issue][issues].
+ This allows us to assign the item to someone
+ and to respond to it in a threaded discussion.
+
+3. If you are comfortable with Git,
+ and would like to add or change material,
+ you can submit a pull request (PR).
+ Instructions for doing this are [included below](#using-github).
+
+## Where to Contribute
+
+1. If you wish to change this lesson,
+ please work in <https://github.com/swcarpentry/FIXME>,
+ which can be viewed at <https://swcarpentry.github.io/FIXME>.
+
+2. If you wish to change the example lesson,
+ please work in <https://github.com/carpentries/lesson-example>,
+ which documents the format of our lessons
+ and can be viewed at <https://carpentries.github.io/lesson-example>.
+
+3. If you wish to change the template used for workshop websites,
+ please work in <https://github.com/carpentries/workshop-template>.
+ The home page of that repository explains how to set up workshop websites,
+ while the extra pages in <https://carpentries.github.io/workshop-template>
+ provide more background on our design choices.
+
+4. If you wish to change CSS style files, tools,
+ or HTML boilerplate for lessons or workshops stored in `_includes` or `_layouts`,
+ please work in <https://github.com/carpentries/styles>.
+
+## What to Contribute
+
+There are many ways to contribute,
+from writing new exercises and improving existing ones
+to updating or filling in the documentation
+and submitting [bug reports][issues]
+about things that don't work, aren't clear, or are missing.
+If you are looking for ideas, please see the 'Issues' tab for
+a list of issues associated with this repository,
+or you may also look at the issues for [Data Carpentry][dc-issues]
+and [Software Carpentry][swc-issues] projects.
+
+Comments on issues and reviews of pull requests are just as welcome:
+we are smarter together than we are on our own.
+Reviews from novices and newcomers are particularly valuable:
+it's easy for people who have been using these lessons for a while
+to forget how impenetrable some of this material can be,
+so fresh eyes are always welcome.
+
+## What *Not* to Contribute
+
+Our lessons already contain more material than we can cover in a typical workshop,
+so we are usually *not* looking for more concepts or tools to add to them.
+As a rule,
+if you want to introduce a new idea,
+you must (a) estimate how long it will take to teach
+and (b) explain what you would take out to make room for it.
+The first encourages contributors to be honest about requirements;
+the second, to think hard about priorities.
+
+We are also not looking for exercises or other material that only run on one platform.
+Our workshops typically contain a mixture of Windows, Mac OS X, and Linux users;
+in order to be usable,
+our lessons must run equally well on all three.
+
+## Using GitHub
+
+If you choose to contribute via GitHub, you may want to look at
+[How to Contribute to an Open Source Project on GitHub][how-contribute].
+To manage changes, we follow [GitHub flow][github-flow].
+Each lesson has two maintainers who review issues and pull requests or encourage others to do so.
+The maintainers are community volunteers and have final say over what gets merged into the lesson.
+To use the web interface for contributing to a lesson:
+
+1. Fork the originating repository to your GitHub profile.
+2. Within your version of the forked repository, move to the `gh-pages` branch and
+create a new branch for each significant change being made.
+3. Navigate to the file(s) you wish to change within the new branches and make revisions as required.
+4. Commit all changed files within the appropriate branches.
+5. Create individual pull requests from each of your changed branches
+to the `gh-pages` branch within the originating repository.
+6. If you receive feedback, make changes using your issue-specific branches of the forked
+repository and the pull requests will update automatically.
+7. Repeat as needed until all feedback has been addressed.
+
+When starting work, please make sure your clone of the originating `gh-pages` branch is up-to-date
+before creating your own revision-specific branch(es) from there.
+Additionally, please only work from your newly-created branch(es) and *not*
+your clone of the originating `gh-pages` branch.
+Lastly, published copies of all the lessons are available in the `gh-pages` branch of the originating
+repository for reference while revising.
+
+## Other Resources
+
+General discussion of [Software Carpentry][swc-site] and [Data Carpentry][dc-site]
+happens on the [discussion mailing list][discuss-list],
+which everyone is welcome to join.
+You can also [reach us by email][email].
+
+[email]: mailto:admin@software-carpentry.org
+[dc-issues]: https://github.com/issues?q=user%3Adatacarpentry
+[dc-lessons]: http://datacarpentry.org/lessons/
+[dc-site]: http://datacarpentry.org/
+[discuss-list]: http://lists.software-carpentry.org/listinfo/discuss
+[github]: https://github.com
+[github-flow]: https://guides.github.com/introduction/flow/
+[github-join]: https://github.com/join
+[how-contribute]: https://egghead.io/series/how-to-contribute-to-an-open-source-project-on-github
+[issues]: https://guides.github.com/features/issues/
+[swc-issues]: https://github.com/issues?q=user%3Aswcarpentry
+[swc-lessons]: https://software-carpentry.org/lessons/
+[swc-site]: https://software-carpentry.org/
--- /dev/null
+# FIXME Lesson title
+
+[![Create a Slack Account with us](https://img.shields.io/badge/Create_Slack_Account-The_Carpentries-071159.svg)](https://swc-slack-invite.herokuapp.com/)
+
+FIXME
+
+## Contributing
+
+We welcome all contributions to improve the lesson! Maintainers will do their best to help you if you have any
+questions, concerns, or experience any difficulties along the way.
+
+We'd like to ask you to familiarize yourself with our [Contribution Guide](CONTRIBUTING.md) and have a look at
+the [more detailed guidelines][lesson-example] on proper formatting, ways to render the lesson locally, and even
+how to write new episodes.
+
+## Maintainer(s)
+
+* FIXME
+
+## Authors
+
+A list of contributors to the lesson can be found in [AUTHORS](AUTHORS)
+
+## Citation
+
+To cite this lesson, please consult with [CITATION](CITATION)
+
+[lesson-example]: https://carpentries.github.io/lesson-example
--- /dev/null
+#------------------------------------------------------------
+# Values for this lesson.
+#------------------------------------------------------------
+
+# Which carpentry is this ("swc", "dc", "lc", or "cp")?
+# swc: Software Carpentry
+# dc: Data Carpentry
+# lc: Library Carpentry
+# cp: Carpentries (to use for instructor traning for instance)
+carpentry: "swc"
+
+# Overall title for pages.
+title: "Lesson Title"
+
+#------------------------------------------------------------
+# Generic settings (should not need to change).
+#------------------------------------------------------------
+
+# What kind of thing is this ("workshop" or "lesson")?
+kind: "lesson"
+
+# Magic to make URLs resolve both locally and on GitHub.
+# See https://help.github.com/articles/repository-metadata-on-github-pages/.
+# Please don't change it: <USERNAME>/<PROJECT> is correct.
+repository: <USERNAME>/<PROJECT>
+
+# Email address, no mailto:
+email: "team@carpentries.org"
+
+# Sites.
+amy_site: "https://amy.software-carpentry.org/workshops"
+carpentries_github: "https://github.com/carpentries"
+carpentries_pages: "https://carpentries.github.io"
+carpentries_site: "https://carpentries.org/"
+dc_site: "http://datacarpentry.org"
+example_repo: "https://github.com/carpentries/lesson-example"
+example_site: "https://carpentries.github.io/lesson-example"
+lc_site: "https://librarycarpentry.github.io/"
+swc_github: "https://github.com/swcarpentry"
+swc_pages: "https://swcarpentry.github.io"
+swc_site: "https://software-carpentry.org"
+template_repo: "https://github.com/carpentries/styles"
+training_site: "https://carpentries.github.io/instructor-training"
+workshop_repo: "https://github.com/carpentries/workshop-template"
+workshop_site: "https://carpentries.github.io/workshop-template"
+
+# Surveys.
+pre_survey: "https://www.surveymonkey.com/r/swc_pre_workshop_v1?workshop_id="
+post_survey: "https://www.surveymonkey.com/r/swc_post_workshop_v1?workshop_id="
+training_post_survey: "https://www.surveymonkey.com/r/post-instructor-training"
+
+# Start time in minutes (0 to be clock-independent, 540 to show a start at 09:00 am).
+start_time: 0
+
+# Specify that things in the episodes collection should be output.
+collections:
+ episodes:
+ output: true
+ permalink: /:path/index.html
+ extras:
+ output: true
+ permalink: /:path/index.html
+
+# Set the default layout for things in the episodes collection.
+defaults:
+ - values:
+ root: .
+ layout: page
+ - scope:
+ path: ""
+ type: episodes
+ values:
+ root: ..
+ layout: episode
+ - scope:
+ path: ""
+ type: extras
+ values:
+ root: ..
+ layout: page
+
+# Files and directories that are not to be copied.
+exclude:
+ - Makefile
+ - bin/
+ - .Rproj.user/
+
+# Turn on built-in syntax highlighting.
+highlighter: rouge
--- /dev/null
+---
+title: "Introduction"
+teaching: 0
+exercises: 0
+questions:
+- "Key question (FIXME)"
+objectives:
+- "First objective. (FIXME)"
+keypoints:
+- "First key point. (FIXME)"
+---
+FIXME
+
+{% include links.md %}
--- /dev/null
+---
+title: About
+---
+{% include carpentries.html %}
+{% include links.md %}
--- /dev/null
+---
+title: Discussion
+---
+FIXME
+
+{% include links.md %}
--- /dev/null
+---
+title: Figures
+---
+<script>
+ window.onload = function() {
+ var lesson_episodes = [
+ {% for episode in site.episodes %}
+ "{{ episode.url}}"{% unless forloop.last %},{% endunless %}
+ {% endfor %}
+ ];
+ var xmlHttp = []; /* Required since we are going to query every episode. */
+ for (i=0; i < lesson_episodes.length; i++) {
+ xmlHttp[i] = new XMLHttpRequest();
+ xmlHttp[i].episode = lesson_episodes[i]; /* To enable use this later. */
+ xmlHttp[i].onreadystatechange = function() {
+ if (this.readyState == 4 && this.status == 200) {
+ var article_here = document.getElementById(this.episode);
+ var parser = new DOMParser();
+ var htmlDoc = parser.parseFromString(this.responseText,"text/html");
+ var htmlDocArticle = htmlDoc.getElementsByTagName("article")[0];
+ article_here.appendChild(htmlDocArticle.getElementsByTagName("h1")[0]);
+ for (let image of htmlDocArticle.getElementsByTagName("img")) {
+ article_here.appendChild(image);
+ }
+ }
+ }
+ episode_url = "{{ page.root }}" + lesson_episodes[i];
+ xmlHttp[i].open("GET", episode_url);
+ xmlHttp[i].send(null);
+ }
+ }
+</script>
+{% comment %}
+Create anchor for each one of the episodes.
+{% endcomment %}
+{% for episode in site.episodes %}
+<article id="{{ episode.url }}"></article>
+{% endfor %}
+
+{% include links.md %}
--- /dev/null
+---
+title: "Instructor Notes"
+---
+FIXME
+
+{% include links.md %}
--- /dev/null
+---
+---
+<script>
+ window.onload = function() {
+ var lesson_episodes = [
+ {% for episode in site.episodes %}
+ "{{ episode.url}}"{% unless forloop.last %},{% endunless %}
+ {% endfor %}
+ ];
+ var xmlHttp = []; /* Required since we are going to query every episode. */
+ for (i=0; i < lesson_episodes.length; i++) {
+ xmlHttp[i] = new XMLHttpRequest();
+ xmlHttp[i].episode = lesson_episodes[i]; /* To enable use this later. */
+ xmlHttp[i].onreadystatechange = function() {
+ if (this.readyState == 4 && this.status == 200) {
+ var article_here = document.getElementById(this.episode);
+ var parser = new DOMParser();
+ var htmlDoc = parser.parseFromString(this.responseText,"text/html");
+ var htmlDocArticle = htmlDoc.getElementsByTagName("article")[0];
+ article_here.innerHTML = htmlDocArticle.innerHTML;
+ }
+ }
+ episode_url = "{{ page.root }}" + lesson_episodes[i];
+ xmlHttp[i].open("GET", episode_url);
+ xmlHttp[i].send(null);
+ }
+ }
+</script>
+{% comment %}
+Create anchor for each one of the episodes.
+{% endcomment %}
+{% for episode in site.episodes %}
+<article id="{{ episode.url }}"></article>
+{% endfor %}
--- /dev/null
+---
+layout: lesson
+root: . # Is the only page that don't follow the partner /:path/index.html
+permalink: index.html # Is the only page that don't follow the partner /:path/index.html
+---
+FIXME: home page introduction
+
+> ## Prerequisites
+>
+> FIXME
+{: .prereq}
+
+{% include links.md %}
--- /dev/null
+---
+layout: reference
+---
+
+## Glossary
+
+FIXME
+
+{% include links.md %}
--- /dev/null
+---
+title: Setup
+---
+FIXME
+
+
+{% include links.md %}
-#!/usr/bin/env python
+#!/usr/bin/env python3
"""
Check lesson files and their contents.
"""
-from __future__ import print_function
-import sys
+
import os
import glob
-import json
import re
-from optparse import OptionParser
+from argparse import ArgumentParser
-from util import Reporter, read_markdown, load_yaml, check_unwanted_files, require, IMAGE_FILE_SUFFIX
+from util import (Reporter, read_markdown, load_yaml, check_unwanted_files,
+ require)
__version__ = '0.3'
# FIXME: We do not yet validate whether any files have the required
# YAML headers, but should in the future.
# The '%' is replaced with the source directory path for checking.
-# Episodes are handled specially, and extra files in '_extras' are also handled specially.
-# This list must include all the Markdown files listed in the 'bin/initialize' script.
+# Episodes are handled specially, and extra files in '_extras' are also handled
+# specially. This list must include all the Markdown files listed in the
+# 'bin/initialize' script.
REQUIRED_FILES = {
- '%/CONDUCT.md': True,
+ '%/CODE_OF_CONDUCT.md': True,
'%/CONTRIBUTING.md': False,
'%/LICENSE.md': True,
'%/README.md': False,
# How long are lines allowed to be?
MAX_LINE_LEN = 100
+
def main():
"""Main driver."""
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_fileset(args.source_dir, args.reporter, list(docs.keys()))
check_unwanted_files(args.source_dir, args.reporter)
- for filename in docs.keys():
+ for filename in list(docs.keys()):
checker = create_checker(args, filename, docs[filename])
checker.check()
args.reporter.report()
+ if args.reporter.messages and not args.permissive:
+ exit(1)
def parse_args():
"""Parse command-line arguments."""
- parser = OptionParser()
- parser.add_option('-l', '--linelen',
- default=False,
- action="store_true",
- dest='line_lengths',
- help='Check line lengths')
- parser.add_option('-p', '--parser',
- 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',
- help='source directory')
- parser.add_option('-w', '--whitespace',
- default=False,
- action="store_true",
- dest='trailing_whitespace',
- help='Check for trailing whitespace')
-
- args, extras = parser.parse_args()
+ parser = ArgumentParser(description="""Check episode files in a lesson.""")
+ parser.add_argument('-l', '--linelen',
+ default=False,
+ action="store_true",
+ dest='line_lengths',
+ help='Check line lengths')
+ parser.add_argument('-p', '--parser',
+ default=None,
+ dest='parser',
+ help='path to Markdown parser')
+ parser.add_argument('-r', '--references',
+ default=None,
+ dest='reference_path',
+ help='path to Markdown file of external references')
+ parser.add_argument('-s', '--source',
+ default=os.curdir,
+ dest='source_dir',
+ help='source directory')
+ parser.add_argument('-w', '--whitespace',
+ default=False,
+ action="store_true",
+ dest='trailing_whitespace',
+ help='Check for trailing whitespace')
+ parser.add_argument('--permissive',
+ default=False,
+ action="store_true",
+ dest='permissive',
+ help='Do not raise an error even if issues are detected')
+
+ args, extras = parser.parse_known_args()
require(args.parser is not None,
'Path to Markdown parser not provided')
require(not extras,
config_file = os.path.join(source_dir, '_config.yml')
config = load_yaml(config_file)
- reporter.check_field(config_file, 'configuration', config, 'kind', 'lesson')
- reporter.check_field(config_file, 'configuration', config, 'carpentry', ('swc', 'dc', 'lc'))
+ reporter.check_field(config_file, 'configuration',
+ config, 'kind', 'lesson')
+ reporter.check_field(config_file, 'configuration',
+ config, 'carpentry', ('swc', 'dc', 'lc', 'cp'))
reporter.check_field(config_file, 'configuration', config, 'title')
reporter.check_field(config_file, 'configuration', config, 'email')
- reporter.check({'values': {'root': '..'}} in config.get('defaults', []),
+ for defaults in [
+ {'values': {'root': '.', 'layout': 'page'}},
+ {'values': {'root': '..', 'layout': 'episode'}, 'scope': {'type': 'episodes', 'path': ''}},
+ {'values': {'root': '..', 'layout': 'page'}, 'scope': {'type': 'extras', 'path': ''}}
+ ]:
+ reporter.check(defaults in config.get('defaults', []),
'configuration',
- '"root" not set to ".." in configuration')
+ '"root" not set to "." in configuration')
def read_references(reporter, ref_path):
if m and m.group(1):
seen.append(m.group(1))
else:
- reporter.add(None, 'Episode {0} has badly-formatted filename', filename)
+ reporter.add(
+ None, 'Episode {0} has badly-formatted filename', filename)
# Check for duplicate episode numbers.
reporter.check(len(seen) == len(set(seen)),
- None,
- 'Duplicate episode numbers {0} vs {1}',
- sorted(seen), sorted(set(seen)))
+ None,
+ 'Duplicate episode numbers {0} vs {1}',
+ sorted(seen), sorted(set(seen)))
# Check that numbers are consecutive.
- seen = [int(s) for s in seen]
- seen.sort()
+ seen = sorted([int(s) for s in seen])
clean = True
for i in range(len(seen) - 1):
clean = clean and ((seen[i+1] - seen[i]) == 1)
for (pat, cls) in CHECKERS:
if pat.search(filename):
return cls(args, filename, **info)
+ return NotImplemented
-
-class CheckBase(object):
+class CheckBase:
"""Base class for checking Markdown files."""
def __init__(self, args, filename, metadata, metadata_len, text, lines, doc):
"""Cache arguments for checking."""
- super(CheckBase, self).__init__()
self.args = args
- self.reporter = self.args.reporter # for convenience
+ self.reporter = self.args.reporter # for convenience
self.filename = filename
self.metadata = metadata
self.metadata_len = metadata_len
self.layout = None
-
def check(self):
"""Run tests."""
self.check_codeblock_classes()
self.check_defined_link_references()
-
def check_metadata(self):
"""Check the YAML metadata."""
'Missing metadata entirely')
if self.metadata and (self.layout is not None):
- self.reporter.check_field(self.filename, 'metadata', self.metadata, 'layout', self.layout)
-
+ self.reporter.check_field(
+ self.filename, 'metadata', self.metadata, 'layout', self.layout)
def check_line_lengths(self):
"""Check the raw text of the lesson body."""
if self.args.line_lengths:
- over = [i for (i, l, n) in self.lines if (n > MAX_LINE_LEN) and (not l.startswith('!'))]
+ over = [i for (i, l, n) in self.lines if (
+ n > MAX_LINE_LEN) and (not l.startswith('!'))]
self.reporter.check(not over,
self.filename,
'Line(s) are too long: {0}',
', '.join([str(i) for i in over]))
-
def check_trailing_whitespace(self):
"""Check for whitespace at the ends of lines."""
if self.args.trailing_whitespace:
- trailing = [i for (i, l, n) in self.lines if P_TRAILING_WHITESPACE.match(l)]
+ trailing = [
+ i for (i, l, n) in self.lines if P_TRAILING_WHITESPACE.match(l)]
self.reporter.check(not trailing,
self.filename,
'Line(s) end with whitespace: {0}',
', '.join([str(i) for i in trailing]))
-
def check_blockquote_classes(self):
"""Check that all blockquotes have known classes."""
- for node in self.find_all(self.doc, {'type' : 'blockquote'}):
+ for node in self.find_all(self.doc, {'type': 'blockquote'}):
cls = self.get_val(node, 'attr', 'class')
self.reporter.check(cls in KNOWN_BLOCKQUOTES,
(self.filename, self.get_loc(node)),
'Unknown or missing blockquote type {0}',
cls)
-
def check_codeblock_classes(self):
"""Check that all code blocks have known classes."""
- for node in self.find_all(self.doc, {'type' : 'codeblock'}):
+ for node in self.find_all(self.doc, {'type': 'codeblock'}):
cls = self.get_val(node, 'attr', 'class')
self.reporter.check(cls in KNOWN_CODEBLOCKS,
(self.filename, self.get_loc(node)),
'Unknown or missing code block type {0}',
cls)
-
def check_defined_link_references(self):
"""Check that defined links resolve in the file.
"""
result = set()
- for node in self.find_all(self.doc, {'type' : 'text'}):
+ for node in self.find_all(self.doc, {'type': 'text'}):
for match in P_INTERNAL_LINK_REF.findall(node['value']):
text = match[0]
link = match[1]
'Internally-defined links may be missing definitions: {0}',
', '.join(sorted(result)))
-
def find_all(self, node, pattern, accum=None):
"""Find all matches for a pattern."""
- assert type(pattern) == dict, 'Patterns must be dictionaries'
+ assert isinstance(pattern, dict), 'Patterns must be dictionaries'
if accum is None:
accum = []
if self.match(node, pattern):
self.find_all(child, pattern, accum)
return accum
-
def match(self, node, pattern):
"""Does this node match the given pattern?"""
if key not in node:
return False
val = pattern[key]
- if type(val) == str:
+ if isinstance(val, str):
if node[key] != val:
return False
- elif type(val) == dict:
+ elif isinstance(val, dict):
if not self.match(node[key], val):
return False
return True
-
- def get_val(self, node, *chain):
+ @staticmethod
+ def get_val(node, *chain):
"""Get value one or more levels down."""
curr = node
break
return curr
-
def get_loc(self, node):
"""Convenience method to get node's line number."""
class CheckNonJekyll(CheckBase):
"""Check a file that isn't translated by Jekyll."""
- def __init__(self, args, filename, metadata, metadata_len, text, lines, doc):
- super(CheckNonJekyll, self).__init__(args, filename, metadata, metadata_len, text, lines, doc)
-
-
def check_metadata(self):
self.reporter.check(self.metadata is None,
self.filename,
"""Check the main index page."""
def __init__(self, args, filename, metadata, metadata_len, text, lines, doc):
- super(CheckIndex, self).__init__(args, filename, metadata, metadata_len, text, lines, doc)
+ super().__init__(args, filename, metadata, metadata_len, text, lines, doc)
self.layout = 'lesson'
def check_metadata(self):
- super(CheckIndex, self).check_metadata()
+ super().check_metadata()
self.reporter.check(self.metadata.get('root', '') == '.',
self.filename,
'Root not set to "."')
class CheckEpisode(CheckBase):
"""Check an episode page."""
- 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()
+ super().check()
self.check_reference_inclusion()
-
def check_metadata(self):
- super(CheckEpisode, self).check_metadata()
+ super().check_metadata()
if self.metadata:
if 'layout' in self.metadata:
if self.metadata['layout'] == 'break':
else:
self.check_metadata_fields(TEACHING_METADATA_FIELDS)
-
def check_metadata_fields(self, expected):
+ """Check metadata fields."""
for (name, type_) in expected:
if name not in self.metadata:
self.reporter.add(self.filename,
'Missing metadata field {0}',
name)
- elif type(self.metadata[name]) != type_:
+ elif not isinstance(self.metadata[name], type_):
self.reporter.add(self.filename,
'"{0}" has wrong type in metadata ({1} instead of {2})',
name, type(self.metadata[name]), type_)
-
def check_reference_inclusion(self):
"""Check that links file has been included."""
"""Check the reference page."""
def __init__(self, args, filename, metadata, metadata_len, text, lines, doc):
- super(CheckReference, self).__init__(args, filename, metadata, metadata_len, text, lines, doc)
+ super().__init__(args, filename, metadata, metadata_len, text, lines, doc)
self.layout = 'reference'
"""Check a generic page."""
def __init__(self, args, filename, metadata, metadata_len, text, lines, doc):
- super(CheckGeneric, self).__init__(args, filename, metadata, metadata_len, text, lines, doc)
- self.layout = 'page'
+ super().__init__(args, filename, metadata, metadata_len, text, lines, doc)
CHECKERS = [
(re.compile(r'index\.md'), CheckIndex),
(re.compile(r'reference\.md'), CheckReference),
(re.compile(r'_episodes/.*\.md'), CheckEpisode),
+ (re.compile(r'aio\.md'), CheckNonJekyll),
(re.compile(r'.*\.md'), CheckGeneric)
]
-#!/usr/bin/env python
+#!/usr/bin/env python3
"""Initialize a newly-created repository."""
-from __future__ import print_function
import sys
import os
-
-ROOT_AUTHORS = '''\
-FIXME: list authors' names and email addresses.
-'''
-
-ROOT_CITATION = '''\
-FIXME: describe how to cite this lesson.
-'''
-
-ROOT_CONTRIBUTING_MD = '''\
-# Contributing
-
-[Software Carpentry][swc-site] and [Data Carpentry][dc-site] are open source projects,
-and we welcome contributions of all kinds:
-new lessons,
-fixes to existing material,
-bug reports,
-and reviews of proposed changes are all welcome.
-
-## Contributor Agreement
-
-By contributing,
-you agree that we may redistribute your work under [our license](LICENSE.md).
-In exchange,
-we will address your issues and/or assess your change proposal as promptly as we can,
-and help you become a member of our community.
-Everyone involved in [Software Carpentry][swc-site] and [Data Carpentry][dc-site]
-agrees to abide by our [code of conduct](CONDUCT.md).
-
-## How to Contribute
-
-The easiest way to get started is to file an issue
-to tell us about a spelling mistake,
-some awkward wording,
-or a factual error.
-This is a good way to introduce yourself
-and to meet some of our community members.
-
-1. If you do not have a [GitHub][github] account,
- you can [send us comments by email][email].
- However,
- we will be able to respond more quickly if you use one of the other methods described below.
-
-2. If you have a [GitHub][github] account,
- or are willing to [create one][github-join],
- but do not know how to use Git,
- you can report problems or suggest improvements by [creating an issue][issues].
- This allows us to assign the item to someone
- and to respond to it in a threaded discussion.
-
-3. If you are comfortable with Git,
- and would like to add or change material,
- you can submit a pull request (PR).
- Instructions for doing this are [included below](#using-github).
-
-## Where to Contribute
-
-1. If you wish to change this lesson,
- please work in <https://github.com/swcarpentry/FIXME>,
- which can be viewed at <https://swcarpentry.github.io/FIXME>.
-
-2. If you wish to change the example lesson,
- please work in <https://github.com/swcarpentry/lesson-example>,
- which documents the format of our lessons
- and can be viewed at <https://swcarpentry.github.io/lesson-example>.
-
-3. If you wish to change the template used for workshop websites,
- please work in <https://github.com/swcarpentry/workshop-template>.
- The home page of that repository explains how to set up workshop websites,
- while the extra pages in <https://swcarpentry.github.io/workshop-template>
- provide more background on our design choices.
-
-4. If you wish to change CSS style files, tools,
- or HTML boilerplate for lessons or workshops stored in `_includes` or `_layouts`,
- please work in <https://github.com/swcarpentry/styles>.
-
-## What to Contribute
-
-There are many ways to contribute,
-from writing new exercises and improving existing ones
-to updating or filling in the documentation
-and and submitting [bug reports][issues]
-about things that don't work, aren't clear, or are missing.
-If you are looking for ideas, please see the 'Issues' tab for
-a list of issues associated with this repository,
-or you may also look at the issues for [Data Carpentry][dc-issues]
-and [Software Carpentry][swc-issues] projects.
-
-Comments on issues and reviews of pull requests are just as welcome:
-we are smarter together than we are on our own.
-Reviews from novices and newcomers are particularly valuable:
-it's easy for people who have been using these lessons for a while
-to forget how impenetrable some of this material can be,
-so fresh eyes are always welcome.
-
-## What *Not* to Contribute
-
-Our lessons already contain more material than we can cover in a typical workshop,
-so we are usually *not* looking for more concepts or tools to add to them.
-As a rule,
-if you want to introduce a new idea,
-you must (a) estimate how long it will take to teach
-and (b) explain what you would take out to make room for it.
-The first encourages contributors to be honest about requirements;
-the second, to think hard about priorities.
-
-We are also not looking for exercises or other material that only run on one platform.
-Our workshops typically contain a mixture of Windows, Mac OS X, and Linux users;
-in order to be usable,
-our lessons must run equally well on all three.
-
-## Using GitHub
-
-If you choose to contribute via GitHub, you may want to look at
-[How to Contribute to an Open Source Project on GitHub][how-contribute].
-To manage changes, we follow [GitHub flow][github-flow].
-Each lesson has two maintainers who review issues and pull requests or encourage others to do so.
-The maintainers are community volunteers and have final say over what gets merged into the lesson.
-To use the web interface for contributing to a lesson:
-
-1. Fork the master repository to your GitHub profile.
-2. Within your version of the forked repository, move to the `gh-pages` branch and
-create a new branch for each significant change being made.
-3. Navigate to the file(s) you wish to change within the new branches and make revisions as required.
-4. Commit all changed files within the appropriate branches.
-5. Create individual pull requests from each of your changed branches
-to the `gh-pages` branch within the master repository.
-6. If you receive feedback, make changes using your issue-specific branches of the forked
-repository and the pull requests will update automatically.
-7. Repeat as needed until all feedback has been addressed.
-
-When starting work, please make sure your clone of the master `gh-pages` branch is up-to-date
-before creating your own revision-specific branch(es) from there.
-Additionally, please only work from your newly-created branch(es) and *not*
-your clone of the master `gh-pages` branch.
-Lastly, published copies of all the lessons are available in the `gh-pages` branch of the master
-repository for reference while revising.
-
-## Other Resources
-
-General discussion of [Software Carpentry][swc-site] and [Data Carpentry][dc-site]
-happens on the [discussion mailing list][discuss-list],
-which everyone is welcome to join.
-You can also [reach us by email][email].
-
-[email]: mailto:admin@software-carpentry.org
-[dc-issues]: https://github.com/issues?q=user%3Adatacarpentry
-[dc-lessons]: http://datacarpentry.org/lessons/
-[dc-site]: http://datacarpentry.org/
-[discuss-list]: http://lists.software-carpentry.org/listinfo/discuss
-[github]: https://github.com
-[github-flow]: https://guides.github.com/introduction/flow/
-[github-join]: https://github.com/join
-[how-contribute]: https://egghead.io/series/how-to-contribute-to-an-open-source-project-on-github
-[issues]: https://guides.github.com/features/issues/
-[swc-issues]: https://github.com/issues?q=user%3Aswcarpentry
-[swc-lessons]: https://software-carpentry.org/lessons/
-[swc-site]: https://software-carpentry.org/
-'''
-
-ROOT_CONFIG_YML = '''\
-#------------------------------------------------------------
-# Values for this lesson.
-#------------------------------------------------------------
-
-# Which carpentry is this ("swc", "dc", or "lc")?
-carpentry: "swc"
-
-# Overall title for pages.
-title: "Lesson Title"
-
-# Contact. This *must* include the protocol: if it's an email
-# address, it must look like "mailto:lessons@software-carpentry.org",
-# or if it's a URL, "https://gitter.im/username/ProjectName".
-email: "mailto:lessons@software-carpentry.org"
-
-#------------------------------------------------------------
-# Generic settings (should not need to change).
-#------------------------------------------------------------
-
-# What kind of thing is this ("workshop" or "lesson")?
-kind: "lesson"
-
-# Magic to make URLs resolve both locally and on GitHub.
-# See https://help.github.com/articles/repository-metadata-on-github-pages/.
-repository: <USERNAME>/<PROJECT>
-
-# Sites.
-amy_site: "https://amy.software-carpentry.org/workshops"
-dc_site: "http://datacarpentry.org"
-swc_github: "https://github.com/swcarpentry"
-swc_site: "https://software-carpentry.org"
-swc_pages: "https://swcarpentry.github.io"
-lc_site: "https://librarycarpentry.github.io/"
-template_repo: "https://github.com/swcarpentry/styles"
-example_repo: "https://github.com/swcarpentry/lesson-example"
-example_site: "https://swcarpentry.github.com/lesson-example"
-workshop_repo: "https://github.com/swcarpentry/workshop-template"
-workshop_site: "https://swcarpentry.github.io/workshop-template"
-training_site: "https://swcarpentry.github.io/instructor-training"
-
-# Surveys.
-pre_survey: "https://www.surveymonkey.com/r/swc_pre_workshop_v1?workshop_id="
-post_survey: "https://www.surveymonkey.com/r/swc_post_workshop_v1?workshop_id="
-training_post_survey: "https://www.surveymonkey.com/r/post-instructor-training"
-
-# Start time in minutes (0 to be clock-independent, 540 to show a start at 09:00 am).
-start_time: 0
-
-# Specify that things in the episodes collection should be output.
-collections:
- episodes:
- output: true
- permalink: /:path/index.html
- extras:
- output: true
- permalink: /:path/index.html
-
-# Set the default layout for things in the episodes collection.
-defaults:
- - values:
- root: ..
- - scope:
- path: ""
- type: episodes
- values:
- layout: episode
-
-# Files and directories that are not to be copied.
-exclude:
- - Makefile
- - bin
-
-# Turn on built-in syntax highlighting.
-highlighter: rouge
-'''
-
-ROOT_INDEX_MD = '''\
----
-layout: lesson
-root: .
-permalink: index.html # Is the only page that don't follow the partner /:path/index.html
----
-FIXME: home page introduction
-
-> ## Prerequisites
->
-> FIXME
-{: .prereq}
-'''
-
-ROOT_REFERENCE_MD = '''\
----
-layout: reference
-root: .
----
-
-## Glossary
-
-FIXME
-'''
-
-ROOT_SETUP_MD = '''\
----
-layout: page
-title: Setup
-root: .
----
-FIXME
-'''
-
-ROOT_AIO_MD = '''\
----
-layout: page
-root: .
----
-<script>
- window.onload = function() {
- var lesson_episodes = [
- {% for episode in site.episodes %}
- "{{ episode.url}}"{% unless forloop.last %},{% endunless %}
- {% endfor %}
- ];
- var xmlHttp = []; /* Required since we are going to query every episode. */
- for (i=0; i < lesson_episodes.length; i++) {
- xmlHttp[i] = new XMLHttpRequest();
- xmlHttp[i].episode = lesson_episodes[i]; /* To enable use this later. */
- xmlHttp[i].onreadystatechange = function() {
- if (this.readyState == 4 && this.status == 200) {
- var article_here = document.getElementById(this.episode);
- var parser = new DOMParser();
- var htmlDoc = parser.parseFromString(this.responseText,"text/html");
- var htmlDocArticle = htmlDoc.getElementsByTagName("article")[0];
- article_here.innerHTML = htmlDocArticle.innerHTML;
- }
- }
- episode_url = "{{ page.root }}" + lesson_episodes[i];
- xmlHttp[i].open("GET", episode_url);
- xmlHttp[i].send(null);
- }
- }
-</script>
-{% comment %}
-Create anchor for each one of the episodes.
-{% endcomment %}
-{% for episode in site.episodes %}
-<article id="{{ episode.url }}"></article>
-{% endfor %}
-'''
-
-EPISODES_INTRODUCTION_MD = '''\
----
-title: "Introduction"
-teaching: 0
-exercises: 0
-questions:
-- "Key question"
-objectives:
-- "First objective."
-keypoints:
-- "First key point."
----
-'''
-
-EXTRAS_ABOUT_MD = '''\
----
-layout: page
-title: About
----
-{% include carpentries.html %}
-'''
-
-EXTRAS_DISCUSS_MD = '''\
----
-layout: page
-title: Discussion
----
-FIXME
-'''
-
-EXTRAS_FIGURES_MD = '''\
----
-layout: page
-title: Figures
----
-<script>
- window.onload = function() {
- var lesson_episodes = [
- {% for episode in site.episodes %}
- "{{ episode.url}}"{% unless forloop.last %},{% endunless %}
- {% endfor %}
- ];
- var xmlHttp = []; /* Required since we are going to query every episode. */
- for (i=0; i < lesson_episodes.length; i++) {
- xmlHttp[i] = new XMLHttpRequest();
- xmlHttp[i].episode = lesson_episodes[i]; /* To enable use this later. */
- xmlHttp[i].onreadystatechange = function() {
- if (this.readyState == 4 && this.status == 200) {
- var article_here = document.getElementById(this.episode);
- var parser = new DOMParser();
- var htmlDoc = parser.parseFromString(this.responseText,"text/html");
- var htmlDocArticle = htmlDoc.getElementsByTagName("article")[0];
- article_here.appendChild(htmlDocArticle.getElementsByTagName("h1")[0]);
- for (let image of htmlDocArticle.getElementsByTagName("img")) {
- article_here.appendChild(image);
- }
- }
- }
- episode_url = "{{ page.root }}" + lesson_episodes[i];
- xmlHttp[i].open("GET", episode_url);
- xmlHttp[i].send(null);
- }
- }
-</script>
-{% comment %}
-Create anchor for each one of the episodes.
-{% endcomment %}
-{% for episode in site.episodes %}
-<article id="{{ episode.url }}"></article>
-{% endfor %}
-'''
-
-EXTRAS_GUIDE_MD = '''\
----
-layout: page
-title: "Instructor Notes"
----
-FIXME
-'''
+import shutil
BOILERPLATE = (
- ('AUTHORS', ROOT_AUTHORS),
- ('CITATION', ROOT_CITATION),
- ('CONTRIBUTING.md', ROOT_CONTRIBUTING_MD),
- ('_config.yml', ROOT_CONFIG_YML),
- ('index.md', ROOT_INDEX_MD),
- ('reference.md', ROOT_REFERENCE_MD),
- ('setup.md', ROOT_SETUP_MD),
- ('aio.md', ROOT_AIO_MD),
- ('_episodes/01-introduction.md', EPISODES_INTRODUCTION_MD),
- ('_extras/about.md', EXTRAS_ABOUT_MD),
- ('_extras/discuss.md', EXTRAS_DISCUSS_MD),
- ('_extras/figures.md', EXTRAS_FIGURES_MD),
- ('_extras/guide.md', EXTRAS_GUIDE_MD),
+ '.travis.yml',
+ 'AUTHORS',
+ 'CITATION',
+ 'CODE_OF_CONDUCT.md',
+ 'CONTRIBUTING.md',
+ 'README.md',
+ '_config.yml',
+ '_episodes/01-introduction.md',
+ '_extras/about.md',
+ '_extras/discuss.md',
+ '_extras/figures.md',
+ '_extras/guide.md',
+ 'aio.md',
+ 'index.md',
+ 'reference.md',
+ 'setup.md',
)
# Check.
errors = False
- for (path, _) in BOILERPLATE:
+ for path in BOILERPLATE:
if os.path.exists(path):
print('Warning: {0} already exists.'.format(path), file=sys.stderr)
errors = True
sys.exit(1)
# Create.
- for (path, content) in BOILERPLATE:
- with open(path, 'w') as writer:
- writer.write(content)
+ for path in BOILERPLATE:
+ shutil.copyfile(
+ "bin/boilerplate/{}".format(path),
+ path
+ )
if __name__ == '__main__':
-#!/usr/bin/env python
+#!/usr/bin/env python3
"""
Check repository settings.
"""
-from __future__ import print_function
+
import sys
import os
from subprocess import Popen, PIPE
import re
-from optparse import OptionParser
+from argparse import ArgumentParser
-from util import Reporter, load_yaml, require
+from util import Reporter, require
# Import this way to produce a more useful error message.
try:
# Expected labels and colors.
EXPECTED = {
- 'bug' : 'bd2c00',
- 'discussion' : 'fc8dc1',
- 'enhancement' : '9cd6dc',
- 'help-wanted' : 'f4fd9c',
- 'instructor-training' : '6e5494',
- 'newcomer-friendly' : 'eec275',
- 'question' : '808040',
- 'template-and-tools' : '2b3990',
- 'work-in-progress' : '7ae78e'
+ 'help wanted': 'dcecc7',
+ 'status:in progress': '9bcc65',
+ 'status:changes requested': '679f38',
+ 'status:wait': 'fff2df',
+ 'status:refer to cac': 'ffdfb2',
+ 'status:need more info': 'ee6c00',
+ 'status:blocked': 'e55100',
+ 'status:out of scope': 'eeeeee',
+ 'status:duplicate': 'bdbdbd',
+ 'type:typo text': 'f8bad0',
+ 'type:bug': 'eb3f79',
+ 'type:formatting': 'ac1357',
+ 'type:template and tools': '7985cb',
+ 'type:instructor guide': '00887a',
+ 'type:discussion': 'b2e5fc',
+ 'type:enhancement': '7fdeea',
+ 'type:clarification': '00acc0',
+ 'type:teaching example': 'ced8dc',
+ 'good first issue': 'ffeb3a',
+ 'high priority': 'd22e2e'
}
args = parse_args()
reporter = Reporter()
- repo_url = get_repo_url(args.source_dir, args.repo_url)
+ repo_url = get_repo_url(args.repo_url)
check_labels(reporter, repo_url)
reporter.report()
Parse command-line arguments.
"""
- parser = OptionParser()
- parser.add_option('-r', '--repo',
- default=None,
- dest='repo_url',
- help='repository URL')
- parser.add_option('-s', '--source',
- default=os.curdir,
- dest='source_dir',
- help='source directory')
-
- args, extras = parser.parse_args()
+ parser = ArgumentParser(description="""Check repository settings.""")
+ parser.add_argument('-r', '--repo',
+ default=None,
+ dest='repo_url',
+ help='repository URL')
+ parser.add_argument('-s', '--source',
+ default=os.curdir,
+ dest='source_dir',
+ help='source directory')
+
+ args, extras = parser.parse_known_args()
require(not extras,
'Unexpected trailing command-line arguments "{0}"'.format(extras))
return args
-def get_repo_url(source_dir, repo_url):
+def get_repo_url(repo_url):
"""
Figure out which repository to query.
"""
# Guess.
cmd = 'git remote -v'
- p = Popen(cmd, shell=True, stdin=PIPE, stdout=PIPE, close_fds=True, universal_newlines=True)
+ p = Popen(cmd, shell=True, stdin=PIPE, stdout=PIPE,
+ close_fds=True, universal_newlines=True)
stdout_data, stderr_data = p.communicate()
stdout_data = stdout_data.split('\n')
matches = [P_GIT_REMOTE.match(line) for line in stdout_data]
'Unexpected output from git remote command: "{0}"'.format(matches))
username = matches[0].group(1)
- require(username, 'empty username in git remote output {0}'.format(matches[0]))
+ require(
+ username, 'empty username in git remote output {0}'.format(matches[0]))
project_name = matches[0].group(2)
- require(username, 'empty project name in git remote output {0}'.format(matches[0]))
+ require(
+ username, 'empty project name in git remote output {0}'.format(matches[0]))
url = F_REPO_URL.format(username, project_name)
return url
overlap = set(EXPECTED.keys()).intersection(set(actual.keys()))
for name in sorted(overlap):
- reporter.check(EXPECTED[name] == actual[name],
+ reporter.check(EXPECTED[name].lower() == actual[name].lower(),
None,
'Color mis-match for label {0} in {1}: expected {2}, found {3}',
name, repo_url, EXPECTED[name], actual[name])
"""
m = P_REPO_URL.match(repo_url)
- require(m, 'repository URL {0} does not match expected pattern'.format(repo_url))
+ require(
+ m, 'repository URL {0} does not match expected pattern'.format(repo_url))
username = m.group(1)
require(username, 'empty username in repository URL {0}'.format(repo_url))
project_name = m.group(2)
- require(username, 'empty project name in repository URL {0}'.format(repo_url))
+ require(
+ username, 'empty project name in repository URL {0}'.format(repo_url))
url = F_API_URL.format(username, project_name)
r = requests.get(url)
+#!/usr/bin/env python3
+
import unittest
import lesson_check
import util
+
class TestFileList(unittest.TestCase):
def setUp(self):
- self.reporter = util.Reporter() ## TODO: refactor reporter class.
+ self.reporter = util.Reporter() # TODO: refactor reporter class.
def test_file_list_has_expected_entries(self):
# For first pass, simply assume that all required files are present
lesson_check.check_fileset('', self.reporter, all_filenames)
self.assertEqual(len(self.reporter.messages), 0)
+
if __name__ == "__main__":
unittest.main()
-from __future__ import print_function
import sys
import os
import json
# (Can't use 'None' because that might be a legitimate value.)
REPORTER_NOT_SET = []
-class Reporter(object):
+
+class Reporter:
"""Collect and report errors."""
def __init__(self):
"""Constructor."""
-
- super(Reporter, self).__init__()
self.messages = []
-
def check_field(self, filename, name, values, key, expected=REPORTER_NOT_SET):
"""Check that a dictionary has an expected value."""
pass
elif type(expected) in (tuple, set, list):
if values[key] not in expected:
- self.add(filename, '{0} {1} value {2} is not in {3}', name, key, values[key], expected)
+ self.add(
+ filename, '{0} {1} value {2} is not in {3}', name, key, values[key], expected)
elif values[key] != expected:
- self.add(filename, '{0} {1} is {2} not {3}', name, key, values[key], expected)
-
+ self.add(filename, '{0} {1} is {2} not {3}',
+ name, key, values[key], expected)
def check(self, condition, location, fmt, *args):
"""Append error if condition not met."""
if not condition:
self.add(location, fmt, *args)
-
def add(self, location, fmt, *args):
"""Append error unilaterally."""
self.messages.append((location, fmt.format(*args)))
+ @staticmethod
+ def pretty(item):
+ location, message = item
+ if isinstance(location, type(None)):
+ return message
+ elif isinstance(location, str):
+ return location + ': ' + message
+ elif isinstance(location, tuple):
+ return '{0}:{1}: '.format(*location) + message
+
+ print('Unknown item "{0}"'.format(item), file=sys.stderr)
+ return NotImplemented
+
+ @staticmethod
+ def key(item):
+ location, message = item
+ if isinstance(location, type(None)):
+ return ('', -1, message)
+ elif isinstance(location, str):
+ return (location, -1, message)
+ elif isinstance(location, tuple):
+ return (location[0], location[1], message)
+
+ print('Unknown item "{0}"'.format(item), file=sys.stderr)
+ return NotImplemented
def report(self, stream=sys.stdout):
"""Report all messages in order."""
if not self.messages:
return
- def pretty(item):
- location, message = item
- if isinstance(location, type(None)):
- return message
- elif isinstance(location, str):
- return location + ': ' + message
- elif isinstance(location, tuple):
- return '{0}:{1}: '.format(*location) + message
- else:
- assert False, 'Unknown item "{0}"'.format(item)
-
- def key(item):
- location, message = item
- if isinstance(location, type(None)):
- return ('', -1, message)
- elif isinstance(location, str):
- return (location, -1, message)
- elif isinstance(location, tuple):
- return (location[0], location[1], message)
- else:
- assert False, 'Unknown item "{0}"'.format(item)
-
- for m in sorted(self.messages, key=key):
- print(pretty(m), file=stream)
+ for m in sorted(self.messages, key=self.key):
+ print(self.pretty(m), file=stream)
def read_markdown(parser, path):
# Split into lines.
metadata_len = 0 if metadata_raw is None else metadata_raw.count('\n')
- lines = [(metadata_len+i+1, line, len(line)) for (i, line) in enumerate(body.split('\n'))]
+ lines = [(metadata_len+i+1, line, len(line))
+ for (i, line) in enumerate(body.split('\n'))]
# Parse Markdown.
cmd = 'ruby {0}'.format(parser)
- p = Popen(cmd, shell=True, stdin=PIPE, stdout=PIPE, close_fds=True, universal_newlines=True)
+ p = Popen(cmd, shell=True, stdin=PIPE, stdout=PIPE,
+ close_fds=True, universal_newlines=True)
stdout_data, stderr_data = p.communicate(body)
doc = json.loads(stdout_data)
metadata_raw = None
metadata_yaml = None
- metadata_len = None
pieces = text.split('---', 2)
if len(pieces) == 3:
try:
metadata_yaml = yaml.load(metadata_raw)
except yaml.YAMLError as e:
- print('Unable to parse YAML header in {0}:\n{1}'.format(path, e), file=sys.stderr)
+ print('Unable to parse YAML header in {0}:\n{1}'.format(
+ path, e), file=sys.stderr)
sys.exit(1)
return metadata_raw, metadata_yaml, text
try:
with open(filename, 'r') as reader:
return yaml.load(reader)
- except (yaml.YAMLError, FileNotFoundError) as e:
- print('Unable to load YAML file {0}:\n{1}'.format(filename, e), file=sys.stderr)
+ except (yaml.YAMLError, IOError) as e:
+ print('Unable to load YAML file {0}:\n{1}'.format(
+ filename, e), file=sys.stderr)
sys.exit(1)
-#!/usr/bin/env python
+#!/usr/bin/env python3
'''Check that a workshop's index.html metadata is valid. See the
docstrings on the checking functions for a summary of the checks.
'''
-from __future__ import print_function
+
import sys
import os
import re
try:
lat, lng = latlng.split(',')
lat = float(lat)
- long = float(lng)
- return (-90.0 <= lat <= 90.0) and (-180.0 <= long <= 180.0)
+ lng = float(lng)
+ return (-90.0 <= lat <= 90.0) and (-180.0 <= lng <= 180.0)
except ValueError:
return False
@look_for_fixme
-def check_email(email):
+def check_emails(emails):
"""
- 'email' must be a comma-separated list of valid email addresses.
+ 'emails' must be a comma-separated list of valid email addresses.
The list may be empty. A valid email address consists of characters,
an '@', and more characters. It should not contain the default contact
"""
return False
else:
return False
-
+
return True
'["First helper", "Second helper",..]'),
'email': (True, check_emails,
- 'contact email list isn\'t a valid list of format ' +
- '["me@example.org", "you@example.org",..] or contains incorrectly formatted email addresses or ' +
- '"{0}".'.format(DEFAULT_CONTACT_EMAIL)),
+ 'contact email list isn\'t a valid list of format ' +
+ '["me@example.org", "you@example.org",..] or contains incorrectly formatted email addresses or ' +
+ '"{0}".'.format(DEFAULT_CONTACT_EMAIL)),
'eventbrite': (False, check_eventbrite, 'Eventbrite key appears invalid'),
}
# REQUIRED is all required categories.
-REQUIRED = set([k for k in HANDLERS if HANDLERS[k][0]])
+REQUIRED = {k for k in HANDLERS if HANDLERS[k][0]}
# OPTIONAL is all optional categories.
-OPTIONAL = set([k for k in HANDLERS if not HANDLERS[k][0]])
+OPTIONAL = {k for k in HANDLERS if not HANDLERS[k][0]}
def check_blank_lines(reporter, raw):
Blank lines are not allowed in category headers.
"""
- lines = [(i, x) for (i, x) in enumerate(raw.strip().split('\n')) if not x.strip()]
+ lines = [(i, x) for (i, x) in enumerate(
+ raw.strip().split('\n')) if not x.strip()]
reporter.check(not lines,
None,
'Blank line(s) in header: {0}',