├── .gitignore ├── .readthedocs.yaml ├── .travis.yml ├── IDEAS ├── LICENSE.txt ├── MANIFEST.in ├── README.rst ├── bin └── labnote ├── docs ├── Makefile ├── _build │ ├── doctrees │ │ ├── configuration.doctree │ │ ├── development.doctree │ │ ├── environment.pickle │ │ ├── features.doctree │ │ ├── index.doctree │ │ └── installation.doctree │ └── html │ │ ├── .buildinfo │ │ ├── _images │ │ ├── category_example.png │ │ └── example_screenshot.png │ │ ├── _sources │ │ ├── configuration.rst.txt │ │ ├── development.rst.txt │ │ ├── features.rst.txt │ │ ├── index.rst.txt │ │ └── installation.rst.txt │ │ ├── _static │ │ ├── alabaster.css │ │ ├── basic.css │ │ ├── custom.css │ │ ├── doctools.js │ │ ├── documentation_options.js │ │ ├── file.png │ │ ├── language_data.js │ │ ├── minus.png │ │ ├── plus.png │ │ ├── pygments.css │ │ ├── searchtools.js │ │ └── sphinx_highlight.js │ │ ├── objects.inv │ │ └── searchindex.js ├── conf.py ├── configuration.rst ├── development.rst ├── features.rst ├── images │ ├── category_example.png │ ├── example_screenshot.png │ └── example_screenshot_bioinf.png ├── index.rst ├── installation.rst └── make.bat ├── examples ├── bioinformatics │ ├── example.config.yml │ └── research │ │ ├── co-expression │ │ └── hsapiens-coex-network │ │ │ └── .labnote │ │ ├── exploratory_data_analysis │ │ ├── biplot_eda.ipynb │ │ └── nmf_comparison.py │ │ ├── host-pathogen_networks │ │ └── hpi-module-network │ │ │ └── hpi_network_construction.py │ │ └── images │ │ ├── biplot.png │ │ ├── coex-network.png │ │ └── hpi-network.png └── darwin │ ├── example.config.yml │ └── research │ ├── animal_behavior │ └── molothrus │ │ └── README.html │ ├── barnacles │ ├── cirripede-morphology │ │ └── README.html │ └── cirripede-taxonomy │ │ └── README.html │ ├── finches │ ├── finch-beak-size-comparison │ │ └── beak_size.py │ ├── finch-foraging-strategies │ │ └── foraging-strategies.py │ └── natural-selection │ │ ├── .labnote │ │ └── thoughts.txt │ ├── images │ ├── 1854_Balanidae_F339.2_figlbp12.jpg │ └── a1417007h.jpg │ └── private │ └── notes.html ├── labnote ├── __init__.py ├── categories.py ├── entry.py ├── notebook.py ├── renderer.py ├── resources │ ├── __init__.py │ ├── css │ │ ├── default.css │ │ ├── normalize.css │ │ └── skeleton.css │ └── img │ │ ├── divider-line1.png │ │ ├── hr-jon-lucas2.jpg │ │ └── libreoffice-impress.png └── templates │ ├── __init__.py │ └── default.html ├── setup.cfg ├── setup.py └── tests ├── conftest.py ├── notebooks └── nb1 │ ├── bar │ ├── one │ ├── three │ └── two │ ├── config.yml │ ├── foo │ ├── 1 │ ├── 2 │ ├── 3 │ ├── a │ ├── b │ └── c │ └── other │ └── misc ├── test_notebook.py └── test_renderer.py /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | *.egg-info 3 | *.html 4 | .cache 5 | .eggs 6 | build 7 | dist 8 | examples/*/research/index.html 9 | examples/*/research/resources 10 | tests/notebooks/*/resources 11 | -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | # .readthedocs.yaml 2 | # Read the Docs configuration file 3 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details 4 | 5 | # Required 6 | version: 2 7 | 8 | # Set the version of Python and other tools you might need 9 | build: 10 | os: ubuntu-22.04 11 | tools: 12 | python: "3.11" 13 | 14 | # Build documentation in the docs/ directory with Sphinx 15 | sphinx: 16 | configuration: docs/conf.py 17 | 18 | # We recommend specifying your dependencies to enable reproducible builds: 19 | # https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html 20 | # python: 21 | # install: 22 | # - requirements: docs/requirements.txt 23 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | dist: xenial 2 | language: python 3 | matrix: 4 | include: 5 | - os: linux 6 | python: 3.5 7 | - os: linux 8 | python: 3.6 9 | - os: linux 10 | python: 3.7 11 | before_script: 12 | - pip install --upgrade pytest 13 | install: 14 | - pip install . 15 | script: py.test 16 | -------------------------------------------------------------------------------- /IDEAS: -------------------------------------------------------------------------------- 1 | Some possible features to implement in the future: 2 | 3 | 1. Use xdg library to lookup XDG_CONFIG_HOME and check for labnote config there 4 | (Linux), or standard locations on Mac (e.g. 5 | http://stackoverflow.com/questions/3373948/equivalents-of-xdg-config-home-and-xdg-data-home-on-mac-os-x) 6 | 2. Add option to output Markdown (e.g. for hosting on Github) 7 | 3. Add option to color entries based on activity (e.g. gradient from blue or 8 | grey -> orange) 9 | 10 | 11 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016, Keith Hughitt 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 2. Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 14 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 17 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | 24 | The views and conclusions contained in the software and documentation are those 25 | of the authors and should not be interpreted as representing official policies, 26 | either expressed or implied, of the FreeBSD Project. 27 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include labnote/templates/* 2 | include labnote/resources/css/* 3 | include labnote/resources/img/* 4 | graft docs 5 | graft examples 6 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | Labnote 2 | ======= 3 | 4 | .. image:: https://travis-ci.org/khughitt/labnote.svg?branch=master 5 | :target: https://travis-ci.org/khughitt/labnote 6 | :alt: Build Status 7 | .. image:: https://readthedocs.org/projects/labnote/badge/?version=latest 8 | :target: http://labnote.readthedocs.org/en/latest/?badge=latest 9 | :alt: Documentation Status 10 | 11 | Overview 12 | -------- 13 | 14 | Labnote is a flexible and lightweight tool for generating 15 | HTML-based `electronic lab 16 | notebooks `__. 17 | 18 | Rather than attempting to provide a unified tool for creating and sharing lab 19 | notebook entries, Labnote simply ties together existing documents and analyses 20 | outputs and builds and creates an HTML index of these resources. 21 | 22 | In short, it helps you go from something like this: 23 | 24 | :: 25 | 26 | ├── co-expression 27 | │   ├── coex-clustering-comparison 28 | │   │   └── README.html 29 | │   ├── hsapiens-coex-network 30 | │   │   └── README.html 31 | │   └── lmajor-coex-network 32 | │   └── README.html 33 | ├── exploratory_data_analysis 34 | │   ├── biplot_eda.ipynb 35 | │   └── nmf_comparison.py 36 | └── host-pathogen_networks 37 |    └── hpi-module-network 38 |    └── hpi_network_construction.py 39 | 40 | To something like this: 41 | 42 | .. figure:: docs/images/example_screenshot_bioinf.png 43 | :alt: A simple lab notebook 44 | 45 | Labnote works by scanning a set of one or more directories for files 46 | matching a pattern that you specify as pertaining to notebook entries 47 | (e.g. a single log, script, or document describing some particular 48 | project or analysis.) It then constructs an HTML table of contents 49 | pointing to each of the matching files. By default, results are sorted 50 | by last-modified date. Categories can be defined and used to separate 51 | entries relating to different topics. 52 | 53 | In order to support as many different work styles as possible, labnote 54 | tries and make as few assumptions as possible about how your files are 55 | organized, and provides configuration options allowing for a wide range of 56 | directory structures and file types. 57 | 58 | Finally, labnote is designed to be extensible. While currently there is 59 | only a single no-frills theme, the 60 | `jinga2 `__ templating system used by 61 | Labnote makes it trivial to create themes. 62 | 63 | Installation 64 | ------------ 65 | 66 | Requirements 67 | ~~~~~~~~~~~~ 68 | 69 | To use labnote, you must have a recent version of 70 | `Python (>=3.3) `__) available on your machine. 71 | 72 | Additionally, labnote requires the following Python libraries: 73 | 74 | - `Beautiful Soup 4 `__ 75 | - `Jinja2 `__ 76 | - `PyYAML `__ 77 | 78 | If you are using pip to install labnote, all of the required 79 | dependencies should be automatically installed for you. 80 | 81 | Labnote is currently aimed at supporting Windows, Linux, and OS X setups. 82 | 83 | Installing labnote 84 | ~~~~~~~~~~~~~~~~~~ 85 | 86 | To install labnote using 87 | `pip `__, run: 88 | 89 | :: 90 | 91 | pip install labnote 92 | 93 | Testing installation 94 | ~~~~~~~~~~~~~~~~~~~~ 95 | 96 | To generate the example notebook, clone the labnote github repo and `cd` to 97 | the `examples/bioinformatics` directory and run: 98 | 99 | :: 100 | 101 | labnote -c example.config.yml \ 102 | -o research/index.html 103 | 104 | A file named ``index.html`` should be outputted to the ``research/`` 105 | sub-directory and should look something like what is shown in the screenshot 106 | above. 107 | 108 | Automating notebook generation 109 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 110 | 111 | Labnote can be easily automated using 112 | `Cron `__. For example, to have labnote 113 | regenerate your lab notebook once a day, run ``crontab -e`` to edit your 114 | user-level cron jobs, and add: 115 | 116 | :: 117 | 118 | @daily labnote 119 | 120 | If you have created a user configuration for labnote in 121 | ``$HOME/.config/labnote/config.yml``, then you are all set. Otherwise simply 122 | add whatever options you would use when calling Labnote from the command-line 123 | to the cronjob, e.g.: 124 | 125 | :: 126 | 127 | @daily labnote -c /path/to/config.yml 128 | 129 | For more information on how to create and customize cron jobs on your system, 130 | see the `Ubuntu Cron Tutorial `__. 131 | 132 | Configuration 133 | ------------- 134 | 135 | Notebook configuration 136 | ~~~~~~~~~~~~~~~~~~~~~~ 137 | 138 | Labnote settings can be specified either via the 139 | command-line at run-time (e.g. 140 | ``labnote -i /some/path/* -o /output/dir``), or using a 141 | `YAML `__ config file, or both. 142 | 143 | By default, Labnote looks for a file named ``config.yml`` located in 144 | ``$HOME/.config/labnote/``. If this file exists, then it will be used 145 | used to configure Labnote's behavior. 146 | 147 | The configuration file should look something like: 148 | 149 | .. code:: yaml 150 | 151 | --- 152 | # General information 153 | title: Lab Notebook 154 | author: Your Name 155 | email: email@address.com 156 | 157 | # Notebook contents 158 | input_dirs: 159 | - /home/user/Dropbox/research/201[2-5]/* 160 | - /home/user/Dropbox/research/2016/*/* 161 | 162 | output_file: /home/user/Dropbox/research/index.html 163 | 164 | include_files: ['*.html', '*.py', '*.ipynb', 'README.*'] 165 | 166 | # Research categories 167 | categories: 168 | 'Sequence Analysis': ['seq', 'dna', 'rna'] 169 | 'Differential Expression': ['dea', 'differential-expression'] 170 | 'Network Analysis': ['network'] 171 | 'Visualization': ['viz'] 172 | 173 | The main settings that are important to define are: 174 | 175 | 1. ``input_dirs`` - One or more 176 | `wildcard `__ 177 | filepath expressions 178 | 2. ``output_file`` - Path to save resulting HTML and its associated files 179 | to. Most often, this will be located some parent directory of the input 180 | directories, possibly in a web-accessible location (e.g. 181 | ``/var/www/index.html`` or ``~/public_html/notebook.html``). 182 | 3. ``include_files`` - Files to link to in your notebook. 183 | 4. ``categories`` - A set of categories you would like to use to 184 | organise your notebook, along with some search strings which can be 185 | used to find project directories that should be placed under those 186 | categories.\* 187 | 188 | You can also point to a config file located in a different location 189 | using the ``-c`` option, e.g. ``labnote -c /path/to/config.yml``. If a 190 | setting is specified both in a configuration file and using a 191 | command-line switch, the option specified on the command-line will take 192 | precedence. 193 | 194 | \*Depending on how you have organized your files, this may be difficult 195 | to setup. It works best if you can normalize your directory names such 196 | that related analyses all include a similar component (e.g. 197 | 'xx-network-analysis'). 198 | 199 | If that is not possible or convenient, Labnote also supports 200 | manually specifying a projects categorization using hidden `.labnote` metafiles 201 | inside each project directory. 202 | 203 | Customizing individual entries 204 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 205 | 206 | In addition to the automatic processing of entries that labnote normally uses 207 | to render notebook entries, directory-specific `.labnote` files can also be 208 | used to control the behavior and appearance of entries. These are YAML files, 209 | and should follow the format: 210 | 211 | .. code:: yaml 212 | 213 | --- 214 | README.html: 215 | title: Custom Title 216 | pipeline.sh: 217 | title: My Interesting Analysis Pipeline 218 | 219 | Furthermore, `.labnote` files can be used to specify additional entry metadata 220 | that can't be automatically detected such as a description of the notebook 221 | entry and links to external resources such as web-pages, presentation slides, 222 | etc: 223 | 224 | .. code:: yaml 225 | 226 | --- 227 | README.html: 228 | title: Custom Title 229 | description: Description of the notebook entry 230 | links: 231 | - http://www.google.com 232 | - research/extra/presentation.ppt 233 | 234 | (NOTE 2016/03/02: the description and external link support haven't been implemented yet, 235 | but should be shortly...) 236 | 237 | Development 238 | ----------- 239 | 240 | Contributing 241 | ~~~~~~~~~~~~ 242 | 243 | The project is just getting started and is changing rapidly. 244 | `Let me know `__ if you have suggestions or 245 | would like to contribute. 246 | 247 | Running tests 248 | ~~~~~~~~~~~~~ 249 | 250 | The easiest way to run the unit tests for labnote is to create a 251 | virtualenv container and run the tests from within there. For example, 252 | if you have 253 | `virtualenvwrapper `__, 254 | you can run: 255 | 256 | :: 257 | 258 | git clone https://github.com/khughitt/labnote && cd labnote 259 | mkvirtualenv labnote 260 | pip install -e . 261 | pip install pytest 262 | hash -r 263 | py.test 264 | 265 | If you already cloned the labnote repo, you can skip the first step 266 | above and simply ``cd`` to the repo directory. 267 | 268 | The ``hash -r`` command in the above is needed after installing py.test 269 | to ensure that the virtualenv version of py.test is used, and not a 270 | system version. 271 | 272 | To run the tests for a different version of Python, you can simply 273 | create a second virtualenv for that version of Python and repeat the 274 | process: 275 | 276 | :: 277 | 278 | mkvirtualenv --python=python3.5 labnote35 279 | 280 | Note that virtualenvwrapper is not needed to run the tests, and the 281 | commands for using the base version of virtualenv are pretty similar. 282 | 283 | TODO 284 | ~~~~ 285 | 286 | Things to be added... 287 | 288 | - Should entries be added via .labnote files, even if they aren't detected in 289 | the search paths? If so, may want to first add entries as-is, and then in a 290 | second round, scan for .labnote files and update affected entries / add new 291 | ones. 292 | - Add option to automatically generate README.html files for each README.md 293 | found (check last modified date to determine whether file should be 294 | regenerated.) 295 | - Add option to automatically convert ipynb files to HTML (use runipy) 296 | - Add option to use icons for entry links. 297 | - Check for git revision and link to repo if on Github 298 | - Add option to show short git commit hashes next to entries which associated 299 | with repos. 300 | - Allow sorting of categories by order in settings (default), name, or 301 | date-modified. 302 | - Add option to show entries in a "journal mode" with all entries displayed 303 | together, sorted from most recent to oldest. Category divisions can either be 304 | hidden entirely, or displayed as (colored) tags to the right side of the 305 | entry titles. 306 | - Color output. 307 | - Add verbose option (default on?) 308 | - Print out warning messages for missing images. 309 | - Print out warning messages for missing titles. 310 | - Print out message about excluded files 311 | - Show entry descriptions as tooltips. 312 | 313 | .. |Build Status| image:: https://travis-ci.org/khughitt/labnote.svg?branch=master 314 | :target: https://travis-ci.org/khughitt/labnote 315 | 316 | -------------------------------------------------------------------------------- /bin/labnote: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | """ 4 | labnote 5 | 6 | Keith Hughitt 7 | """ 8 | from labnote import Notebook 9 | 10 | def main(): 11 | notebook = Notebook() 12 | notebook.render() 13 | 14 | if __name__ == '__main__': 15 | main() 16 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | BUILDDIR = _build 9 | 10 | # User-friendly check for sphinx-build 11 | ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) 12 | $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) 13 | endif 14 | 15 | # Internal variables. 16 | PAPEROPT_a4 = -D latex_paper_size=a4 17 | PAPEROPT_letter = -D latex_paper_size=letter 18 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 19 | # the i18n builder cannot share the environment and doctrees with the others 20 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 21 | 22 | .PHONY: help 23 | help: 24 | @echo "Please use \`make ' where is one of" 25 | @echo " html to make standalone HTML files" 26 | @echo " dirhtml to make HTML files named index.html in directories" 27 | @echo " singlehtml to make a single large HTML file" 28 | @echo " pickle to make pickle files" 29 | @echo " json to make JSON files" 30 | @echo " htmlhelp to make HTML files and a HTML help project" 31 | @echo " qthelp to make HTML files and a qthelp project" 32 | @echo " applehelp to make an Apple Help Book" 33 | @echo " devhelp to make HTML files and a Devhelp project" 34 | @echo " epub to make an epub" 35 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 36 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 37 | @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" 38 | @echo " text to make text files" 39 | @echo " man to make manual pages" 40 | @echo " texinfo to make Texinfo files" 41 | @echo " info to make Texinfo files and run them through makeinfo" 42 | @echo " gettext to make PO message catalogs" 43 | @echo " changes to make an overview of all changed/added/deprecated items" 44 | @echo " xml to make Docutils-native XML files" 45 | @echo " pseudoxml to make pseudoxml-XML files for display purposes" 46 | @echo " linkcheck to check all external links for integrity" 47 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 48 | @echo " coverage to run coverage check of the documentation (if enabled)" 49 | 50 | .PHONY: clean 51 | clean: 52 | rm -rf $(BUILDDIR)/* 53 | 54 | .PHONY: html 55 | html: 56 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 57 | @echo 58 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 59 | 60 | .PHONY: dirhtml 61 | dirhtml: 62 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 63 | @echo 64 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 65 | 66 | .PHONY: singlehtml 67 | singlehtml: 68 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 69 | @echo 70 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 71 | 72 | .PHONY: pickle 73 | pickle: 74 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 75 | @echo 76 | @echo "Build finished; now you can process the pickle files." 77 | 78 | .PHONY: json 79 | json: 80 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 81 | @echo 82 | @echo "Build finished; now you can process the JSON files." 83 | 84 | .PHONY: htmlhelp 85 | htmlhelp: 86 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 87 | @echo 88 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 89 | ".hhp project file in $(BUILDDIR)/htmlhelp." 90 | 91 | .PHONY: qthelp 92 | qthelp: 93 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 94 | @echo 95 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 96 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 97 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Labnote.qhcp" 98 | @echo "To view the help file:" 99 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Labnote.qhc" 100 | 101 | .PHONY: applehelp 102 | applehelp: 103 | $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp 104 | @echo 105 | @echo "Build finished. The help book is in $(BUILDDIR)/applehelp." 106 | @echo "N.B. You won't be able to view it unless you put it in" \ 107 | "~/Library/Documentation/Help or install it in your application" \ 108 | "bundle." 109 | 110 | .PHONY: devhelp 111 | devhelp: 112 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 113 | @echo 114 | @echo "Build finished." 115 | @echo "To view the help file:" 116 | @echo "# mkdir -p $$HOME/.local/share/devhelp/Labnote" 117 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Labnote" 118 | @echo "# devhelp" 119 | 120 | .PHONY: epub 121 | epub: 122 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 123 | @echo 124 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 125 | 126 | .PHONY: latex 127 | latex: 128 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 129 | @echo 130 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 131 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 132 | "(use \`make latexpdf' here to do that automatically)." 133 | 134 | .PHONY: latexpdf 135 | latexpdf: 136 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 137 | @echo "Running LaTeX files through pdflatex..." 138 | $(MAKE) -C $(BUILDDIR)/latex all-pdf 139 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 140 | 141 | .PHONY: latexpdfja 142 | latexpdfja: 143 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 144 | @echo "Running LaTeX files through platex and dvipdfmx..." 145 | $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja 146 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 147 | 148 | .PHONY: text 149 | text: 150 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 151 | @echo 152 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 153 | 154 | .PHONY: man 155 | man: 156 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 157 | @echo 158 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 159 | 160 | .PHONY: texinfo 161 | texinfo: 162 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 163 | @echo 164 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." 165 | @echo "Run \`make' in that directory to run these through makeinfo" \ 166 | "(use \`make info' here to do that automatically)." 167 | 168 | .PHONY: info 169 | info: 170 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 171 | @echo "Running Texinfo files through makeinfo..." 172 | make -C $(BUILDDIR)/texinfo info 173 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." 174 | 175 | .PHONY: gettext 176 | gettext: 177 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale 178 | @echo 179 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." 180 | 181 | .PHONY: changes 182 | changes: 183 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 184 | @echo 185 | @echo "The overview file is in $(BUILDDIR)/changes." 186 | 187 | .PHONY: linkcheck 188 | linkcheck: 189 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 190 | @echo 191 | @echo "Link check complete; look for any errors in the above output " \ 192 | "or in $(BUILDDIR)/linkcheck/output.txt." 193 | 194 | .PHONY: doctest 195 | doctest: 196 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 197 | @echo "Testing of doctests in the sources finished, look at the " \ 198 | "results in $(BUILDDIR)/doctest/output.txt." 199 | 200 | .PHONY: coverage 201 | coverage: 202 | $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage 203 | @echo "Testing of coverage in the sources finished, look at the " \ 204 | "results in $(BUILDDIR)/coverage/python.txt." 205 | 206 | .PHONY: xml 207 | xml: 208 | $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml 209 | @echo 210 | @echo "Build finished. The XML files are in $(BUILDDIR)/xml." 211 | 212 | .PHONY: pseudoxml 213 | pseudoxml: 214 | $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml 215 | @echo 216 | @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." 217 | -------------------------------------------------------------------------------- /docs/_build/doctrees/configuration.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/khughitt/labnote/0c7815936f955f3609628f8a5cab28b6ed2451c4/docs/_build/doctrees/configuration.doctree -------------------------------------------------------------------------------- /docs/_build/doctrees/development.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/khughitt/labnote/0c7815936f955f3609628f8a5cab28b6ed2451c4/docs/_build/doctrees/development.doctree -------------------------------------------------------------------------------- /docs/_build/doctrees/environment.pickle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/khughitt/labnote/0c7815936f955f3609628f8a5cab28b6ed2451c4/docs/_build/doctrees/environment.pickle -------------------------------------------------------------------------------- /docs/_build/doctrees/features.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/khughitt/labnote/0c7815936f955f3609628f8a5cab28b6ed2451c4/docs/_build/doctrees/features.doctree -------------------------------------------------------------------------------- /docs/_build/doctrees/index.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/khughitt/labnote/0c7815936f955f3609628f8a5cab28b6ed2451c4/docs/_build/doctrees/index.doctree -------------------------------------------------------------------------------- /docs/_build/doctrees/installation.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/khughitt/labnote/0c7815936f955f3609628f8a5cab28b6ed2451c4/docs/_build/doctrees/installation.doctree -------------------------------------------------------------------------------- /docs/_build/html/.buildinfo: -------------------------------------------------------------------------------- 1 | # Sphinx build info version 1 2 | # This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. 3 | config: 38c917da433fbb0c77d3d40ec969746c 4 | tags: 645f666f9bcd5a90fca523b33c5a78b7 5 | -------------------------------------------------------------------------------- /docs/_build/html/_images/category_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/khughitt/labnote/0c7815936f955f3609628f8a5cab28b6ed2451c4/docs/_build/html/_images/category_example.png -------------------------------------------------------------------------------- /docs/_build/html/_images/example_screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/khughitt/labnote/0c7815936f955f3609628f8a5cab28b6ed2451c4/docs/_build/html/_images/example_screenshot.png -------------------------------------------------------------------------------- /docs/_build/html/_sources/configuration.rst.txt: -------------------------------------------------------------------------------- 1 | .. _configuration: 2 | 3 | Configuration 4 | ============= 5 | 6 | Overview 7 | -------- 8 | 9 | Labnote settings can be specified either via the command-line at run-time (e.g. 10 | ``labnote -i /some/path/* -o /output/dir``), or using a `YAML 11 | `__ config file, or both. 12 | 13 | By default, Labnote looks for a file named ``config.yml`` located in 14 | ``$HOME/.config/labnote/``. If this file exists, then it will be used 15 | used to configure Labnote's behavior. 16 | 17 | The configuration file should look something like: 18 | 19 | .. code:: yaml 20 | 21 | --- 22 | # General information 23 | title: Lab Notebook 24 | author: Your Name 25 | email: email@address.com 26 | 27 | # Notebook contents 28 | input_dirs: 29 | - /home/user/Dropbox/research/201[2-5]/* 30 | - /home/user/Dropbox/research/2016/*/* 31 | 32 | output_file: /home/user/Dropbox/research/index.html 33 | 34 | include_files: ['*.html', '*.py', '*.ipynb', 'README.*'] 35 | 36 | # Research categories 37 | categories: 38 | 'Sequence Analysis': ['seq', 'dna', 'rna'] 39 | 'Differential Expression': ['dea', 'differential-expression'] 40 | 'Network Analysis': ['network'] 41 | 'Visualization': ['viz'] 42 | 43 | The main settings that are important to define are: 44 | 45 | 1. ``input_dirs`` - One or more 46 | `wildcard `__ 47 | filepath expressions 48 | 2. ``output_file`` - Path to save resulting HTML and its associated files 49 | to. Most often, this will be located some parent directory of the input 50 | directories, possibly in a web-accessible location (e.g. 51 | ``/var/www/index.html`` or ``~/public_html/notebook.html``). 52 | 3. ``include_files`` - Files to link to in your notebook. 53 | 4. ``categories`` - A set of categories you would like to use to 54 | organise your notebook, along with some search strings which can be 55 | used to find project directories that should be placed under those 56 | categories.\* 57 | 58 | You can also point to a config file located in a different location 59 | using the ``-c`` option, e.g. ``labnote -c /path/to/config.yml``. If a 60 | setting is specified both in a configuration file and using a 61 | command-line switch, the option specified on the command-line will take 62 | precedence. 63 | 64 | \*Depending on how you have organized your files, this may be difficult 65 | to setup. It works best if you can normalize your directory names such 66 | that related analyses all include a similar component (e.g. 67 | 'xx-network-analysis'). 68 | 69 | If that is not possible or convenient, Labnote also supports manually 70 | specifying a projects categorization using hidden `.labnote` metafiles inside 71 | each project directory. 72 | 73 | The ``--print-config`` option can be used to generate an empty config file 74 | which you can then customize to your liking, e.g.: 75 | 76 | :: 77 | 78 | mkdir -p $HOME/.config/labnote 79 | labnote --print-config > $HOME/.config/labnote/config.yml 80 | 81 | Configuration Options 82 | --------------------- 83 | 84 | Below is a complete list of Labnote configuration options and how they are 85 | expected to be used. 86 | 87 | Except for ``input_dirs`` and ``output_file``, all configuration parameters are 88 | optional. 89 | 90 | General options 91 | ~~~~~~~~~~~~~~~ 92 | 93 | - ``author`` Author name (string). 94 | - ``email`` Author contact email address (string). 95 | - ``title`` Lab notebook title (string). 96 | 97 | Files to include 98 | ~~~~~~~~~~~~~~~~ 99 | 100 | - ``input_dirs`` List of one or more absolute or relative directory paths which 101 | should be scanned for files corresponding to notebook entries. Only those 102 | filenames which match the patterns specified in the ``include_files`` option 103 | described below will be added. 104 | - ``include_files``: List of filename wildcard expressions specifying the files 105 | to be included in the notebook. 106 | - ``exclude`` List of strings indicating files which should not be included in 107 | the notebook. If any part of a file's directory or filename matches a string 108 | in the ``exclude`` list, it will be skipped. 109 | - ``external``: A dictionary containing one or more external links which should 110 | be included as notebook entries. Each external entry must include a ``url`` 111 | key indicating where the entry can be accessed, and, optionally, a 112 | ``category``. 113 | 114 | Example: 115 | 116 | .. code:: yaml 117 | 118 | external: 119 | 'Interactive network visualization': 120 | category: 'Shiny' 121 | url: 'http://user.shinyapps.io/network-viz' 122 | 'Interactive time series viewer': 123 | category: 'Shiny' 124 | url: 'http://server.com:3838/time-series-viewer' 125 | 126 | Categories 127 | ~~~~~~~~~~ 128 | 129 | - ``categories`` List of dictionaries describing how entries should be grouped. 130 | 131 | For each category, a list of one or more patterns should be specified. When 132 | entries are added to the notebook, the entry directory and filenames will be 133 | compared against the category patterns, and the entry will be assigned to 134 | the first category that it matches, or else default to an "Other" category. 135 | 136 | In addition to the entry search patterns, each category may also include an 137 | image and description field, which may be used by themes during the notebook 138 | HTML generation. 139 | 140 | Example: 141 | 142 | .. code:: yaml 143 | 144 | categories: 145 | 'Network Analysis': ['network', 'edge-betweenness', 'topology'] 146 | 'Differential Expression': 147 | patterns: ['diffexpr', 'dea'] 148 | image: 'resources/user/img/maplot.jpg' 149 | 'Regulatory Element Detection': 150 | patterns: ['motif', 'regulatory-elem', 'random-forest'] 151 | image: '2015/15-random-forest-gene-reg/output/motif_example.png' 152 | 153 | In the above example, for the first category, only the search patterns to be 154 | used for notebook entry membership determination are specified. The next two 155 | categories also specify and image to use. 156 | 157 | Appearance 158 | ~~~~~~~~~~ 159 | 160 | - ``theme`` String indicating the Labnote theme to be used for the rendered 161 | notebook. Currently there is only one theme provided: "default". 162 | - ``sort_categories_by_date`` Boolean indicating whether the categories should 163 | be sorted by the date of the most recent entry within each category. 164 | (default: true) 165 | - ``sort_entries_by_date``: Boolean indicating whether the entries within each 166 | category should be sorted by date. If true, entries within each category will 167 | appear in decreasing order by date last modified, otherwise they will be 168 | sorted alphanumerically. (default: false). 169 | 170 | Output 171 | ~~~~~~ 172 | 173 | - ``output_file``: Filepath where notebook should be written. Notebook resources 174 | (CSS, images, etc.) will also be copied to the directory containing the 175 | indicated output file. 176 | - ``url_prefix``: A string prefix to be prepended to each entry URL. This can be 177 | used, for example, to point to a remotely-hosted version of the notebook. 178 | - ``user_css``: Custom CSS file to include in the output HTML (string). 179 | - ``user_js``: Custom JavaScript file to include in the output HTML (string). 180 | 181 | -------------------------------------------------------------------------------- /docs/_build/html/_sources/development.rst.txt: -------------------------------------------------------------------------------- 1 | Development 2 | ----------- 3 | 4 | Contributing 5 | ~~~~~~~~~~~~ 6 | 7 | There are a few different ways you can contribute to the development of 8 | Labnote. The easiest way is to simply fork the Labnote repo, add whatever 9 | fixes or functionality you would like, and submit a pull request with the 10 | changes. 11 | 12 | If you are adding new functionality, please be sure to write unit tests where 13 | relevant. 14 | 15 | If you have ideas or suggestions, but aren't able to implement them yourself, 16 | feel free to `contact me `__, or open up an issue with 17 | your idea or suggestion on the `Labnote Github repo 18 | `__. 19 | 20 | Running tests 21 | ~~~~~~~~~~~~~ 22 | 23 | The easiest way to run the unit tests for labnote is to create a 24 | virtualenv container and run the tests from within there. For example, 25 | if you have 26 | `virtualenvwrapper `__, 27 | you can run: 28 | 29 | :: 30 | 31 | git clone https://github.com/khughitt/labnote && cd labnote 32 | mkvirtualenv labnote 33 | pip install -e . 34 | pip install pytest 35 | hash -r 36 | py.test 37 | 38 | If you already cloned the labnote repo, you can skip the first step 39 | above and simply ``cd`` to the repo directory. 40 | 41 | The ``hash -r`` command in the above is needed after installing py.test 42 | to ensure that the virtualenv version of py.test is used, and not a 43 | system version. 44 | 45 | To run the tests for a different version of Python, you can simply 46 | create a second virtualenv for that version of Python and repeat the 47 | process: 48 | 49 | :: 50 | 51 | mkvirtualenv --python=python3.3 labnote33 52 | 53 | Note that virtualenvwrapper is not needed to run the tests, and the 54 | commands for using the base version of virtualenv are pretty similar. 55 | 56 | Creating new themes 57 | ~~~~~~~~~~~~~~~~~~~ 58 | 59 | More on this later... 60 | 61 | 62 | -------------------------------------------------------------------------------- /docs/_build/html/_sources/features.rst.txt: -------------------------------------------------------------------------------- 1 | .. _features: 2 | 3 | Features 4 | -------- 5 | 6 | Supported file formats 7 | ~~~~~~~~~~~~~~~~~~~~~~ 8 | 9 | While labnote is capable of including arbitrary file types, there are several 10 | file types which Labnote natively recognizes and is able to provide extended 11 | functionality for such as automatic title determination, and (in the future), 12 | real-time conversion. For each of these, Labnote will attempt to automatically choose an appropriate 13 | title. 14 | 15 | HTML 16 | '''' 17 | 18 | For HTML files, the `` element will be used. 19 | 20 | Python 21 | '''''' 22 | 23 | For Python files, the first line of a file docstring will be used as a title. 24 | 25 | IPython Notebook 26 | '''''''''''''''' 27 | 28 | For IPython (Jupyter) notebooks, a custom `metadata` field will be used if 29 | found. This can be added by editing the JSON source for the notebook, and 30 | adding a ``labnote`` section to the ``metadata`` JSON block: 31 | 32 | .. code:: json 33 | 34 | "metadata": { 35 | "kernelspec": { 36 | "display_name": "Python 3", 37 | "language": "python", 38 | "name": "python3" 39 | }, 40 | "labnote": { 41 | "title": "Notebook title" 42 | }, 43 | 44 | Recent versions of Jupyter notebook include an a built-in metadata editor, 45 | although any plain-text editor such as Vim will also work fine. 46 | 47 | Customizing categories 48 | ~~~~~~~~~~~~~~~~~~~~~~ 49 | 50 | Categories are used by Labnote to group related notebook entries. Each category 51 | may be assigned an image and description which may be used by Labnote themes to 52 | provide a custom appearance for each section of the notebook. See the 53 | :ref:`configuration` section on categories for more information. 54 | 55 | .. figure:: images/category_example.png 56 | :alt: Example of a notebook category which includes an image. 57 | 58 | Customizing individual entries 59 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 60 | 61 | In addition to the automatic processing of entries that labnote normally uses 62 | to render notebook entries, directory-specific `.labnote` files can also be 63 | used to control the behavior and appearance of entries. These are YAML files, 64 | and should follow the format: 65 | 66 | .. code:: yaml 67 | 68 | --- 69 | README.html: 70 | title: Custom Title 71 | pipeline.sh: 72 | title: My Interesting Analysis Pipeline 73 | 74 | Furthermore, `.labnote` files can be used to specify additional entry metadata 75 | that can't be automatically detected such as a description of the notebook 76 | entry and links to external resources such as web-pages, presentation slides, 77 | etc: 78 | 79 | .. code:: yaml 80 | 81 | --- 82 | README.html: 83 | title: Custom Title 84 | description: Description of the notebook entry 85 | links: 86 | - http://www.google.com 87 | - research/extra/presentation.ppt 88 | 89 | 90 | Themes 91 | ~~~~~~ 92 | 93 | Default theme 94 | ''''''''''''' 95 | 96 | Currently Labnote ships with a single theme, creatively named "default". This 97 | is the theme that is shown in the example screenshot. It uses the `Skeleton 98 | <http://getskeleton.com/>`__ CSS framework to produce a simple three column 99 | layout with notebook entries in the center and, optionally, category images on 100 | either side of the entries. 101 | 102 | Additional themes may be included in the future and users are welcome to 103 | contribute their own themes. See the development guide for more information on 104 | creating new themes. 105 | 106 | Customizing themes with CSS/JS 107 | '''''''''''''''''''''''''''''' 108 | 109 | Lab note provides two configuration option which allow you to specify custom 110 | CSS or JavaScript files which should be included, regardless of which theme 111 | you are using: 112 | 113 | * ``user_css`` 114 | * ``user_js`` 115 | 116 | These may be included in your config.yml, or specified at run-time, and will 117 | result in the specified files being included in the outputted HTML. 118 | 119 | For example, if you wanted to change the font used for the category headers, 120 | you could create a file named 'custom.css' containing: 121 | 122 | .. code:: css 123 | 124 | .category-header { 125 | font-family: 'helvetica', 'sans-serif'; 126 | } 127 | 128 | Edit your config so that `user_css` provides a path to `custom.css`, relative 129 | to where your notebook output is saved, and regenerate the notebook. Your 130 | category headers should now use the Helvetica font instead of the cursive font 131 | currently used. 132 | 133 | -------------------------------------------------------------------------------- /docs/_build/html/_sources/index.rst.txt: -------------------------------------------------------------------------------- 1 | :tocdepth: 3 2 | 3 | Welcome to Labnote's documentation! 4 | =================================== 5 | 6 | Overview 7 | ======== 8 | 9 | Labnote is a flexible and lightweight tool for generating 10 | HTML-based `electronic lab 11 | notebooks <https://en.wikipedia.org/wiki/Electronic_lab_notebook>`__. 12 | 13 | Rather than attempting to provide a unified tool for creating and sharing lab 14 | notebook entries, Labnote simply ties together existing documents and analyses 15 | outputs and builds and creates an HTML index of these resources. 16 | 17 | In short, it helps you go from something like this: 18 | 19 | :: 20 | 21 | ├── animal_behavior 22 | │   └── molothrus 23 | │   └── README.html 24 | ├── barnacles 25 | │   ├── cirripede-morphology 26 | │   │   └── README.html 27 | │   └── cirripede-taxonomy 28 | │   └── README.html 29 | └── finches 30 | ├── finch-beak-size-comparison 31 | │   └── beak_size.py 32 | ├── finch-foraging-strategies 33 | │   └── foraging-strategies.py 34 | └── natural-selection 35 | └── thoughts.txt 36 | 37 | To something like this: 38 | 39 | .. figure:: images/example_screenshot.png 40 | :alt: A simple lab notebook 41 | 42 | Labnote works by scanning a set of one or more directories for files 43 | matching a pattern that you specify as pertaining to notebook entries 44 | (e.g. a single log, script, or document describing some particular 45 | project or analysis.) It then constructs an HTML table of contents 46 | pointing to each of the matching files. By default, results are sorted 47 | by last-modified date. Categories can be defined and used to separate 48 | entries relating to different topics. 49 | 50 | In order to support as many different work styles as possible, labnote 51 | tries and make as few assumptions as possible about how your files are 52 | organized, and provides configuration options allowing for a wide range of 53 | directory structures and file types. 54 | 55 | Finally, labnote is designed to be extensible. While currently there is 56 | only a single no-frills theme, the 57 | `jinga2 <http://jinja.pocoo.org/docs/dev/>`__ templating system used by 58 | Labnote makes it trivial to create themes. 59 | 60 | Documentation 61 | ============= 62 | 63 | .. toctree:: 64 | :maxdepth: 2 65 | 66 | installation.rst 67 | configuration.rst 68 | features.rst 69 | development.rst 70 | 71 | -------------------------------------------------------------------------------- /docs/_build/html/_sources/installation.rst.txt: -------------------------------------------------------------------------------- 1 | Installation 2 | ============ 3 | 4 | Requirements 5 | ------------ 6 | 7 | To use labnote, you must have a recent version of 8 | `Python (>=3.3) <https://www.python.org/>`__) available on your machine. 9 | 10 | Additionally, labnote requires the following Python libraries: 11 | 12 | - `Beautiful Soup 4 <http://www.crummy.com/software/BeautifulSoup/>`__ 13 | - `Jinja2 <http://jinja.pocoo.org/docs/dev/>`__ 14 | - `PyYAML <http://pyyaml.org/>`__ 15 | 16 | If you are using pip to install labnote, all of the required 17 | dependencies should be automatically installed for you. 18 | 19 | Labnote is currently aimed at supporting Windows, Linux, and OS X setups. 20 | 21 | Installing labnote 22 | ------------------ 23 | 24 | To install the latest stable version of Labnote using 25 | `pip <https://docs.python.org/3.5/installing/index.html>`__, run: 26 | 27 | :: 28 | 29 | pip install labnote 30 | 31 | To install the most recent version of Labnote from Github, run: 32 | 33 | :: 34 | 35 | pip install git+https://github.com/khughitt/labnote 36 | 37 | To see a full list of options available for labnote, run ``labnote -h``: 38 | 39 | :: 40 | 41 | usage: labnote [-h] [-c CONFIG] [-i INPUT_DIRS [INPUT_DIRS ...]] 42 | [-o OUTPUT_FILE] [-u URL_PREFIX] [--print-config] 43 | [--user-css USER_CSS] [--user-js USER_JS] 44 | 45 | Generate HTML lab notebook. 46 | 47 | optional arguments: 48 | -h, --help show this help message and exit 49 | -c CONFIG, --config CONFIG 50 | Configuration filepath. (Will use configuration in 51 | $HOME/.config/labnote/config.yml, if it exists.) 52 | -i INPUT_DIRS [INPUT_DIRS ...], --input-dirs INPUT_DIRS [INPUT_DIRS ...] 53 | Input directory(s) containing notebook entries. 54 | -o OUTPUT_FILE, --output-file OUTPUT_FILE 55 | Location to output notebook HTML to. 56 | -u URL_PREFIX, --url-prefix URL_PREFIX 57 | Prefix to add to each entry URL. (Default: "") 58 | --print-config Prints the default configuration for Labnote to screen 59 | --user-css USER_CSS Custom stylesheet to use. 60 | --user-js USER_JS Custom javascript file to use. 61 | 62 | Testing installation 63 | -------------------- 64 | 65 | To generate the example notebook, ``cd`` to the labnote source directory and 66 | run: 67 | 68 | :: 69 | 70 | labnote -c example/example.config.yml \ 71 | -i example/research/*/* \ 72 | -o example/research/index.html 73 | 74 | If the installation is working, you should see output to the screen which looks 75 | like: 76 | 77 | :: 78 | 79 | - Using configuration: example/example.config.yml 80 | - Starting Labnote 81 | LOADING 82 | - Scanning for notebook entries in example/research/animal_behavior/molothrus 83 | - Scanning for notebook entries in example/research/barnacles/cirripede-morphology 84 | - Scanning for notebook entries in example/research/barnacles/cirripede-taxonomy 85 | - Scanning for notebook entries in example/research/finches/finch-beak-size-comparison 86 | - Scanning for notebook entries in example/research/finches/finch-foraging-strategies 87 | - Scanning for notebook entries in example/research/finches/natural-selection 88 | - Scanning for notebook entries in example/research/images/1854_Balanidae_F339.2_figlbp12.jpg 89 | - Scanning for notebook entries in example/research/images/a1417007h.jpg 90 | - Scanning for notebook entries in example/research/private/notes.html 91 | - Scanning for notebook entries in example/research/resources/css 92 | - Scanning for notebook entries in example/research/resources/img 93 | * Adding example/research/animal_behavior/molothrus/README.html 94 | * Adding example/research/barnacles/cirripede-morphology/README.html 95 | * Adding example/research/barnacles/cirripede-taxonomy/README.html 96 | * Adding example/research/finches/finch-beak-size-comparison/beak_size.py 97 | * Adding example/research/finches/finch-foraging-strategies/foraging-strategies.py 98 | * Adding example/research/finches/natural-selection/thoughts.txt 99 | - Finished 100 | - Generating notebook HTML 101 | - Saving notebook to example/research/index.html 102 | 103 | A file named ``index.html`` will be outputted ``example/`` 104 | directory and should look something like what is shown in the screenshot 105 | from the overview section of the documentation. 106 | 107 | Now you are ready to configure Labnote for your own files. 108 | 109 | Automating notebook generation 110 | ------------------------------ 111 | 112 | The easiest way to keep your lab notebook up-to-date is to set Labnote so that 113 | it is run every day. 114 | 115 | Labnote can be easily automated using 116 | `Cron <https://en.wikipedia.org/wiki/Cron>`__. For example, to have labnote 117 | regenerate your lab notebook once a day, run ``crontab -e`` to edit your 118 | user-level cron jobs, and add: 119 | 120 | :: 121 | 122 | @daily labnote 123 | 124 | If you have created a user configuration for labnote in 125 | ``$HOME/.config/labnote/config.yml``, then you are all set. Otherwise simply 126 | add whatever options you would use when calling Labnote from the command-line 127 | to the cronjob, e.g.: 128 | 129 | :: 130 | 131 | @daily labnote -c /path/to/config.yml 132 | 133 | For more information on how to create and customize cron jobs on your system, 134 | see the `Ubuntu Cron Tutorial <https://help.ubuntu.com/community/CronHowto>`__. 135 | 136 | -------------------------------------------------------------------------------- /docs/_build/html/_static/alabaster.css: -------------------------------------------------------------------------------- 1 | @import url("basic.css"); 2 | 3 | /* -- page layout ----------------------------------------------------------- */ 4 | 5 | body { 6 | font-family: Georgia, serif; 7 | font-size: 17px; 8 | background-color: #fff; 9 | color: #000; 10 | margin: 0; 11 | padding: 0; 12 | } 13 | 14 | 15 | div.document { 16 | width: 940px; 17 | margin: 30px auto 0 auto; 18 | } 19 | 20 | div.documentwrapper { 21 | float: left; 22 | width: 100%; 23 | } 24 | 25 | div.bodywrapper { 26 | margin: 0 0 0 220px; 27 | } 28 | 29 | div.sphinxsidebar { 30 | width: 220px; 31 | font-size: 14px; 32 | line-height: 1.5; 33 | } 34 | 35 | hr { 36 | border: 1px solid #B1B4B6; 37 | } 38 | 39 | div.body { 40 | background-color: #fff; 41 | color: #3E4349; 42 | padding: 0 30px 0 30px; 43 | } 44 | 45 | div.body > .section { 46 | text-align: left; 47 | } 48 | 49 | div.footer { 50 | width: 940px; 51 | margin: 20px auto 30px auto; 52 | font-size: 14px; 53 | color: #888; 54 | text-align: right; 55 | } 56 | 57 | div.footer a { 58 | color: #888; 59 | } 60 | 61 | p.caption { 62 | font-family: inherit; 63 | font-size: inherit; 64 | } 65 | 66 | 67 | div.relations { 68 | display: none; 69 | } 70 | 71 | 72 | div.sphinxsidebar a { 73 | color: #444; 74 | text-decoration: none; 75 | border-bottom: 1px dotted #999; 76 | } 77 | 78 | div.sphinxsidebar a:hover { 79 | border-bottom: 1px solid #999; 80 | } 81 | 82 | div.sphinxsidebarwrapper { 83 | padding: 18px 10px; 84 | } 85 | 86 | div.sphinxsidebarwrapper p.logo { 87 | padding: 0; 88 | margin: -10px 0 0 0px; 89 | text-align: center; 90 | } 91 | 92 | div.sphinxsidebarwrapper h1.logo { 93 | margin-top: -10px; 94 | text-align: center; 95 | margin-bottom: 5px; 96 | text-align: left; 97 | } 98 | 99 | div.sphinxsidebarwrapper h1.logo-name { 100 | margin-top: 0px; 101 | } 102 | 103 | div.sphinxsidebarwrapper p.blurb { 104 | margin-top: 0; 105 | font-style: normal; 106 | } 107 | 108 | div.sphinxsidebar h3, 109 | div.sphinxsidebar h4 { 110 | font-family: Georgia, serif; 111 | color: #444; 112 | font-size: 24px; 113 | font-weight: normal; 114 | margin: 0 0 5px 0; 115 | padding: 0; 116 | } 117 | 118 | div.sphinxsidebar h4 { 119 | font-size: 20px; 120 | } 121 | 122 | div.sphinxsidebar h3 a { 123 | color: #444; 124 | } 125 | 126 | div.sphinxsidebar p.logo a, 127 | div.sphinxsidebar h3 a, 128 | div.sphinxsidebar p.logo a:hover, 129 | div.sphinxsidebar h3 a:hover { 130 | border: none; 131 | } 132 | 133 | div.sphinxsidebar p { 134 | color: #555; 135 | margin: 10px 0; 136 | } 137 | 138 | div.sphinxsidebar ul { 139 | margin: 10px 0; 140 | padding: 0; 141 | color: #000; 142 | } 143 | 144 | div.sphinxsidebar ul li.toctree-l1 > a { 145 | font-size: 120%; 146 | } 147 | 148 | div.sphinxsidebar ul li.toctree-l2 > a { 149 | font-size: 110%; 150 | } 151 | 152 | div.sphinxsidebar input { 153 | border: 1px solid #CCC; 154 | font-family: Georgia, serif; 155 | font-size: 1em; 156 | } 157 | 158 | div.sphinxsidebar hr { 159 | border: none; 160 | height: 1px; 161 | color: #AAA; 162 | background: #AAA; 163 | 164 | text-align: left; 165 | margin-left: 0; 166 | width: 50%; 167 | } 168 | 169 | div.sphinxsidebar .badge { 170 | border-bottom: none; 171 | } 172 | 173 | div.sphinxsidebar .badge:hover { 174 | border-bottom: none; 175 | } 176 | 177 | /* To address an issue with donation coming after search */ 178 | div.sphinxsidebar h3.donation { 179 | margin-top: 10px; 180 | } 181 | 182 | /* -- body styles ----------------------------------------------------------- */ 183 | 184 | a { 185 | color: #004B6B; 186 | text-decoration: underline; 187 | } 188 | 189 | a:hover { 190 | color: #6D4100; 191 | text-decoration: underline; 192 | } 193 | 194 | div.body h1, 195 | div.body h2, 196 | div.body h3, 197 | div.body h4, 198 | div.body h5, 199 | div.body h6 { 200 | font-family: Georgia, serif; 201 | font-weight: normal; 202 | margin: 30px 0px 10px 0px; 203 | padding: 0; 204 | } 205 | 206 | div.body h1 { margin-top: 0; padding-top: 0; font-size: 240%; } 207 | div.body h2 { font-size: 180%; } 208 | div.body h3 { font-size: 150%; } 209 | div.body h4 { font-size: 130%; } 210 | div.body h5 { font-size: 100%; } 211 | div.body h6 { font-size: 100%; } 212 | 213 | a.headerlink { 214 | color: #DDD; 215 | padding: 0 4px; 216 | text-decoration: none; 217 | } 218 | 219 | a.headerlink:hover { 220 | color: #444; 221 | background: #EAEAEA; 222 | } 223 | 224 | div.body p, div.body dd, div.body li { 225 | line-height: 1.4em; 226 | } 227 | 228 | div.admonition { 229 | margin: 20px 0px; 230 | padding: 10px 30px; 231 | background-color: #EEE; 232 | border: 1px solid #CCC; 233 | } 234 | 235 | div.admonition tt.xref, div.admonition code.xref, div.admonition a tt { 236 | background-color: #FBFBFB; 237 | border-bottom: 1px solid #fafafa; 238 | } 239 | 240 | div.admonition p.admonition-title { 241 | font-family: Georgia, serif; 242 | font-weight: normal; 243 | font-size: 24px; 244 | margin: 0 0 10px 0; 245 | padding: 0; 246 | line-height: 1; 247 | } 248 | 249 | div.admonition p.last { 250 | margin-bottom: 0; 251 | } 252 | 253 | div.highlight { 254 | background-color: #fff; 255 | } 256 | 257 | dt:target, .highlight { 258 | background: #FAF3E8; 259 | } 260 | 261 | div.warning { 262 | background-color: #FCC; 263 | border: 1px solid #FAA; 264 | } 265 | 266 | div.danger { 267 | background-color: #FCC; 268 | border: 1px solid #FAA; 269 | -moz-box-shadow: 2px 2px 4px #D52C2C; 270 | -webkit-box-shadow: 2px 2px 4px #D52C2C; 271 | box-shadow: 2px 2px 4px #D52C2C; 272 | } 273 | 274 | div.error { 275 | background-color: #FCC; 276 | border: 1px solid #FAA; 277 | -moz-box-shadow: 2px 2px 4px #D52C2C; 278 | -webkit-box-shadow: 2px 2px 4px #D52C2C; 279 | box-shadow: 2px 2px 4px #D52C2C; 280 | } 281 | 282 | div.caution { 283 | background-color: #FCC; 284 | border: 1px solid #FAA; 285 | } 286 | 287 | div.attention { 288 | background-color: #FCC; 289 | border: 1px solid #FAA; 290 | } 291 | 292 | div.important { 293 | background-color: #EEE; 294 | border: 1px solid #CCC; 295 | } 296 | 297 | div.note { 298 | background-color: #EEE; 299 | border: 1px solid #CCC; 300 | } 301 | 302 | div.tip { 303 | background-color: #EEE; 304 | border: 1px solid #CCC; 305 | } 306 | 307 | div.hint { 308 | background-color: #EEE; 309 | border: 1px solid #CCC; 310 | } 311 | 312 | div.seealso { 313 | background-color: #EEE; 314 | border: 1px solid #CCC; 315 | } 316 | 317 | div.topic { 318 | background-color: #EEE; 319 | } 320 | 321 | p.admonition-title { 322 | display: inline; 323 | } 324 | 325 | p.admonition-title:after { 326 | content: ":"; 327 | } 328 | 329 | pre, tt, code { 330 | font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; 331 | font-size: 0.9em; 332 | } 333 | 334 | .hll { 335 | background-color: #FFC; 336 | margin: 0 -12px; 337 | padding: 0 12px; 338 | display: block; 339 | } 340 | 341 | img.screenshot { 342 | } 343 | 344 | tt.descname, tt.descclassname, code.descname, code.descclassname { 345 | font-size: 0.95em; 346 | } 347 | 348 | tt.descname, code.descname { 349 | padding-right: 0.08em; 350 | } 351 | 352 | img.screenshot { 353 | -moz-box-shadow: 2px 2px 4px #EEE; 354 | -webkit-box-shadow: 2px 2px 4px #EEE; 355 | box-shadow: 2px 2px 4px #EEE; 356 | } 357 | 358 | table.docutils { 359 | border: 1px solid #888; 360 | -moz-box-shadow: 2px 2px 4px #EEE; 361 | -webkit-box-shadow: 2px 2px 4px #EEE; 362 | box-shadow: 2px 2px 4px #EEE; 363 | } 364 | 365 | table.docutils td, table.docutils th { 366 | border: 1px solid #888; 367 | padding: 0.25em 0.7em; 368 | } 369 | 370 | table.field-list, table.footnote { 371 | border: none; 372 | -moz-box-shadow: none; 373 | -webkit-box-shadow: none; 374 | box-shadow: none; 375 | } 376 | 377 | table.footnote { 378 | margin: 15px 0; 379 | width: 100%; 380 | border: 1px solid #EEE; 381 | background: #FDFDFD; 382 | font-size: 0.9em; 383 | } 384 | 385 | table.footnote + table.footnote { 386 | margin-top: -15px; 387 | border-top: none; 388 | } 389 | 390 | table.field-list th { 391 | padding: 0 0.8em 0 0; 392 | } 393 | 394 | table.field-list td { 395 | padding: 0; 396 | } 397 | 398 | table.field-list p { 399 | margin-bottom: 0.8em; 400 | } 401 | 402 | /* Cloned from 403 | * https://github.com/sphinx-doc/sphinx/commit/ef60dbfce09286b20b7385333d63a60321784e68 404 | */ 405 | .field-name { 406 | -moz-hyphens: manual; 407 | -ms-hyphens: manual; 408 | -webkit-hyphens: manual; 409 | hyphens: manual; 410 | } 411 | 412 | table.footnote td.label { 413 | width: .1px; 414 | padding: 0.3em 0 0.3em 0.5em; 415 | } 416 | 417 | table.footnote td { 418 | padding: 0.3em 0.5em; 419 | } 420 | 421 | dl { 422 | margin-left: 0; 423 | margin-right: 0; 424 | margin-top: 0; 425 | padding: 0; 426 | } 427 | 428 | dl dd { 429 | margin-left: 30px; 430 | } 431 | 432 | blockquote { 433 | margin: 0 0 0 30px; 434 | padding: 0; 435 | } 436 | 437 | ul, ol { 438 | /* Matches the 30px from the narrow-screen "li > ul" selector below */ 439 | margin: 10px 0 10px 30px; 440 | padding: 0; 441 | } 442 | 443 | pre { 444 | background: #EEE; 445 | padding: 7px 30px; 446 | margin: 15px 0px; 447 | line-height: 1.3em; 448 | } 449 | 450 | div.viewcode-block:target { 451 | background: #ffd; 452 | } 453 | 454 | dl pre, blockquote pre, li pre { 455 | margin-left: 0; 456 | padding-left: 30px; 457 | } 458 | 459 | tt, code { 460 | background-color: #ecf0f3; 461 | color: #222; 462 | /* padding: 1px 2px; */ 463 | } 464 | 465 | tt.xref, code.xref, a tt { 466 | background-color: #FBFBFB; 467 | border-bottom: 1px solid #fff; 468 | } 469 | 470 | a.reference { 471 | text-decoration: none; 472 | border-bottom: 1px dotted #004B6B; 473 | } 474 | 475 | /* Don't put an underline on images */ 476 | a.image-reference, a.image-reference:hover { 477 | border-bottom: none; 478 | } 479 | 480 | a.reference:hover { 481 | border-bottom: 1px solid #6D4100; 482 | } 483 | 484 | a.footnote-reference { 485 | text-decoration: none; 486 | font-size: 0.7em; 487 | vertical-align: top; 488 | border-bottom: 1px dotted #004B6B; 489 | } 490 | 491 | a.footnote-reference:hover { 492 | border-bottom: 1px solid #6D4100; 493 | } 494 | 495 | a:hover tt, a:hover code { 496 | background: #EEE; 497 | } 498 | 499 | 500 | @media screen and (max-width: 870px) { 501 | 502 | div.sphinxsidebar { 503 | display: none; 504 | } 505 | 506 | div.document { 507 | width: 100%; 508 | 509 | } 510 | 511 | div.documentwrapper { 512 | margin-left: 0; 513 | margin-top: 0; 514 | margin-right: 0; 515 | margin-bottom: 0; 516 | } 517 | 518 | div.bodywrapper { 519 | margin-top: 0; 520 | margin-right: 0; 521 | margin-bottom: 0; 522 | margin-left: 0; 523 | } 524 | 525 | ul { 526 | margin-left: 0; 527 | } 528 | 529 | li > ul { 530 | /* Matches the 30px from the "ul, ol" selector above */ 531 | margin-left: 30px; 532 | } 533 | 534 | .document { 535 | width: auto; 536 | } 537 | 538 | .footer { 539 | width: auto; 540 | } 541 | 542 | .bodywrapper { 543 | margin: 0; 544 | } 545 | 546 | .footer { 547 | width: auto; 548 | } 549 | 550 | .github { 551 | display: none; 552 | } 553 | 554 | 555 | 556 | } 557 | 558 | 559 | 560 | @media screen and (max-width: 875px) { 561 | 562 | body { 563 | margin: 0; 564 | padding: 20px 30px; 565 | } 566 | 567 | div.documentwrapper { 568 | float: none; 569 | background: #fff; 570 | } 571 | 572 | div.sphinxsidebar { 573 | display: block; 574 | float: none; 575 | width: 102.5%; 576 | margin: 50px -30px -20px -30px; 577 | padding: 10px 20px; 578 | background: #333; 579 | color: #FFF; 580 | } 581 | 582 | div.sphinxsidebar h3, div.sphinxsidebar h4, div.sphinxsidebar p, 583 | div.sphinxsidebar h3 a { 584 | color: #fff; 585 | } 586 | 587 | div.sphinxsidebar a { 588 | color: #AAA; 589 | } 590 | 591 | div.sphinxsidebar p.logo { 592 | display: none; 593 | } 594 | 595 | div.document { 596 | width: 100%; 597 | margin: 0; 598 | } 599 | 600 | div.footer { 601 | display: none; 602 | } 603 | 604 | div.bodywrapper { 605 | margin: 0; 606 | } 607 | 608 | div.body { 609 | min-height: 0; 610 | padding: 0; 611 | } 612 | 613 | .rtd_doc_footer { 614 | display: none; 615 | } 616 | 617 | .document { 618 | width: auto; 619 | } 620 | 621 | .footer { 622 | width: auto; 623 | } 624 | 625 | .footer { 626 | width: auto; 627 | } 628 | 629 | .github { 630 | display: none; 631 | } 632 | } 633 | 634 | 635 | /* misc. */ 636 | 637 | .revsys-inline { 638 | display: none!important; 639 | } 640 | 641 | /* Make nested-list/multi-paragraph items look better in Releases changelog 642 | * pages. Without this, docutils' magical list fuckery causes inconsistent 643 | * formatting between different release sub-lists. 644 | */ 645 | div#changelog > div.section > ul > li > p:only-child { 646 | margin-bottom: 0; 647 | } 648 | 649 | /* Hide fugly table cell borders in ..bibliography:: directive output */ 650 | table.docutils.citation, table.docutils.citation td, table.docutils.citation th { 651 | border: none; 652 | /* Below needed in some edge cases; if not applied, bottom shadows appear */ 653 | -moz-box-shadow: none; 654 | -webkit-box-shadow: none; 655 | box-shadow: none; 656 | } 657 | 658 | 659 | /* relbar */ 660 | 661 | .related { 662 | line-height: 30px; 663 | width: 100%; 664 | font-size: 0.9rem; 665 | } 666 | 667 | .related.top { 668 | border-bottom: 1px solid #EEE; 669 | margin-bottom: 20px; 670 | } 671 | 672 | .related.bottom { 673 | border-top: 1px solid #EEE; 674 | } 675 | 676 | .related ul { 677 | padding: 0; 678 | margin: 0; 679 | list-style: none; 680 | } 681 | 682 | .related li { 683 | display: inline; 684 | } 685 | 686 | nav#rellinks { 687 | float: right; 688 | } 689 | 690 | nav#rellinks li+li:before { 691 | content: "|"; 692 | } 693 | 694 | nav#breadcrumbs li+li:before { 695 | content: "\00BB"; 696 | } 697 | 698 | /* Hide certain items when printing */ 699 | @media print { 700 | div.related { 701 | display: none; 702 | } 703 | } -------------------------------------------------------------------------------- /docs/_build/html/_static/custom.css: -------------------------------------------------------------------------------- 1 | /* This file intentionally left blank. */ 2 | -------------------------------------------------------------------------------- /docs/_build/html/_static/doctools.js: -------------------------------------------------------------------------------- 1 | /* 2 | * doctools.js 3 | * ~~~~~~~~~~~ 4 | * 5 | * Base JavaScript utilities for all Sphinx HTML documentation. 6 | * 7 | * :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS. 8 | * :license: BSD, see LICENSE for details. 9 | * 10 | */ 11 | "use strict"; 12 | 13 | const BLACKLISTED_KEY_CONTROL_ELEMENTS = new Set([ 14 | "TEXTAREA", 15 | "INPUT", 16 | "SELECT", 17 | "BUTTON", 18 | ]); 19 | 20 | const _ready = (callback) => { 21 | if (document.readyState !== "loading") { 22 | callback(); 23 | } else { 24 | document.addEventListener("DOMContentLoaded", callback); 25 | } 26 | }; 27 | 28 | /** 29 | * Small JavaScript module for the documentation. 30 | */ 31 | const Documentation = { 32 | init: () => { 33 | Documentation.initDomainIndexTable(); 34 | Documentation.initOnKeyListeners(); 35 | }, 36 | 37 | /** 38 | * i18n support 39 | */ 40 | TRANSLATIONS: {}, 41 | PLURAL_EXPR: (n) => (n === 1 ? 0 : 1), 42 | LOCALE: "unknown", 43 | 44 | // gettext and ngettext don't access this so that the functions 45 | // can safely bound to a different name (_ = Documentation.gettext) 46 | gettext: (string) => { 47 | const translated = Documentation.TRANSLATIONS[string]; 48 | switch (typeof translated) { 49 | case "undefined": 50 | return string; // no translation 51 | case "string": 52 | return translated; // translation exists 53 | default: 54 | return translated[0]; // (singular, plural) translation tuple exists 55 | } 56 | }, 57 | 58 | ngettext: (singular, plural, n) => { 59 | const translated = Documentation.TRANSLATIONS[singular]; 60 | if (typeof translated !== "undefined") 61 | return translated[Documentation.PLURAL_EXPR(n)]; 62 | return n === 1 ? singular : plural; 63 | }, 64 | 65 | addTranslations: (catalog) => { 66 | Object.assign(Documentation.TRANSLATIONS, catalog.messages); 67 | Documentation.PLURAL_EXPR = new Function( 68 | "n", 69 | `return (${catalog.plural_expr})` 70 | ); 71 | Documentation.LOCALE = catalog.locale; 72 | }, 73 | 74 | /** 75 | * helper function to focus on search bar 76 | */ 77 | focusSearchBar: () => { 78 | document.querySelectorAll("input[name=q]")[0]?.focus(); 79 | }, 80 | 81 | /** 82 | * Initialise the domain index toggle buttons 83 | */ 84 | initDomainIndexTable: () => { 85 | const toggler = (el) => { 86 | const idNumber = el.id.substr(7); 87 | const toggledRows = document.querySelectorAll(`tr.cg-${idNumber}`); 88 | if (el.src.substr(-9) === "minus.png") { 89 | el.src = `${el.src.substr(0, el.src.length - 9)}plus.png`; 90 | toggledRows.forEach((el) => (el.style.display = "none")); 91 | } else { 92 | el.src = `${el.src.substr(0, el.src.length - 8)}minus.png`; 93 | toggledRows.forEach((el) => (el.style.display = "")); 94 | } 95 | }; 96 | 97 | const togglerElements = document.querySelectorAll("img.toggler"); 98 | togglerElements.forEach((el) => 99 | el.addEventListener("click", (event) => toggler(event.currentTarget)) 100 | ); 101 | togglerElements.forEach((el) => (el.style.display = "")); 102 | if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) togglerElements.forEach(toggler); 103 | }, 104 | 105 | initOnKeyListeners: () => { 106 | // only install a listener if it is really needed 107 | if ( 108 | !DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS && 109 | !DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS 110 | ) 111 | return; 112 | 113 | document.addEventListener("keydown", (event) => { 114 | // bail for input elements 115 | if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return; 116 | // bail with special keys 117 | if (event.altKey || event.ctrlKey || event.metaKey) return; 118 | 119 | if (!event.shiftKey) { 120 | switch (event.key) { 121 | case "ArrowLeft": 122 | if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; 123 | 124 | const prevLink = document.querySelector('link[rel="prev"]'); 125 | if (prevLink && prevLink.href) { 126 | window.location.href = prevLink.href; 127 | event.preventDefault(); 128 | } 129 | break; 130 | case "ArrowRight": 131 | if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; 132 | 133 | const nextLink = document.querySelector('link[rel="next"]'); 134 | if (nextLink && nextLink.href) { 135 | window.location.href = nextLink.href; 136 | event.preventDefault(); 137 | } 138 | break; 139 | } 140 | } 141 | 142 | // some keyboard layouts may need Shift to get / 143 | switch (event.key) { 144 | case "/": 145 | if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) break; 146 | Documentation.focusSearchBar(); 147 | event.preventDefault(); 148 | } 149 | }); 150 | }, 151 | }; 152 | 153 | // quick alias for translations 154 | const _ = Documentation.gettext; 155 | 156 | _ready(Documentation.init); 157 | -------------------------------------------------------------------------------- /docs/_build/html/_static/documentation_options.js: -------------------------------------------------------------------------------- 1 | var DOCUMENTATION_OPTIONS = { 2 | URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'), 3 | VERSION: '0.9', 4 | LANGUAGE: 'en', 5 | COLLAPSE_INDEX: false, 6 | BUILDER: 'html', 7 | FILE_SUFFIX: '.html', 8 | LINK_SUFFIX: '.html', 9 | HAS_SOURCE: true, 10 | SOURCELINK_SUFFIX: '.txt', 11 | NAVIGATION_WITH_KEYS: false, 12 | SHOW_SEARCH_SUMMARY: true, 13 | ENABLE_SEARCH_SHORTCUTS: true, 14 | }; -------------------------------------------------------------------------------- /docs/_build/html/_static/file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/khughitt/labnote/0c7815936f955f3609628f8a5cab28b6ed2451c4/docs/_build/html/_static/file.png -------------------------------------------------------------------------------- /docs/_build/html/_static/language_data.js: -------------------------------------------------------------------------------- 1 | /* 2 | * language_data.js 3 | * ~~~~~~~~~~~~~~~~ 4 | * 5 | * This script contains the language-specific data used by searchtools.js, 6 | * namely the list of stopwords, stemmer, scorer and splitter. 7 | * 8 | * :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS. 9 | * :license: BSD, see LICENSE for details. 10 | * 11 | */ 12 | 13 | var stopwords = ["a", "and", "are", "as", "at", "be", "but", "by", "for", "if", "in", "into", "is", "it", "near", "no", "not", "of", "on", "or", "such", "that", "the", "their", "then", "there", "these", "they", "this", "to", "was", "will", "with"]; 14 | 15 | 16 | /* Non-minified version is copied as a separate JS file, is available */ 17 | 18 | /** 19 | * Porter Stemmer 20 | */ 21 | var Stemmer = function() { 22 | 23 | var step2list = { 24 | ational: 'ate', 25 | tional: 'tion', 26 | enci: 'ence', 27 | anci: 'ance', 28 | izer: 'ize', 29 | bli: 'ble', 30 | alli: 'al', 31 | entli: 'ent', 32 | eli: 'e', 33 | ousli: 'ous', 34 | ization: 'ize', 35 | ation: 'ate', 36 | ator: 'ate', 37 | alism: 'al', 38 | iveness: 'ive', 39 | fulness: 'ful', 40 | ousness: 'ous', 41 | aliti: 'al', 42 | iviti: 'ive', 43 | biliti: 'ble', 44 | logi: 'log' 45 | }; 46 | 47 | var step3list = { 48 | icate: 'ic', 49 | ative: '', 50 | alize: 'al', 51 | iciti: 'ic', 52 | ical: 'ic', 53 | ful: '', 54 | ness: '' 55 | }; 56 | 57 | var c = "[^aeiou]"; // consonant 58 | var v = "[aeiouy]"; // vowel 59 | var C = c + "[^aeiouy]*"; // consonant sequence 60 | var V = v + "[aeiou]*"; // vowel sequence 61 | 62 | var mgr0 = "^(" + C + ")?" + V + C; // [C]VC... is m>0 63 | var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1 64 | var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1 65 | var s_v = "^(" + C + ")?" + v; // vowel in stem 66 | 67 | this.stemWord = function (w) { 68 | var stem; 69 | var suffix; 70 | var firstch; 71 | var origword = w; 72 | 73 | if (w.length < 3) 74 | return w; 75 | 76 | var re; 77 | var re2; 78 | var re3; 79 | var re4; 80 | 81 | firstch = w.substr(0,1); 82 | if (firstch == "y") 83 | w = firstch.toUpperCase() + w.substr(1); 84 | 85 | // Step 1a 86 | re = /^(.+?)(ss|i)es$/; 87 | re2 = /^(.+?)([^s])s$/; 88 | 89 | if (re.test(w)) 90 | w = w.replace(re,"$1$2"); 91 | else if (re2.test(w)) 92 | w = w.replace(re2,"$1$2"); 93 | 94 | // Step 1b 95 | re = /^(.+?)eed$/; 96 | re2 = /^(.+?)(ed|ing)$/; 97 | if (re.test(w)) { 98 | var fp = re.exec(w); 99 | re = new RegExp(mgr0); 100 | if (re.test(fp[1])) { 101 | re = /.$/; 102 | w = w.replace(re,""); 103 | } 104 | } 105 | else if (re2.test(w)) { 106 | var fp = re2.exec(w); 107 | stem = fp[1]; 108 | re2 = new RegExp(s_v); 109 | if (re2.test(stem)) { 110 | w = stem; 111 | re2 = /(at|bl|iz)$/; 112 | re3 = new RegExp("([^aeiouylsz])\\1$"); 113 | re4 = new RegExp("^" + C + v + "[^aeiouwxy]$"); 114 | if (re2.test(w)) 115 | w = w + "e"; 116 | else if (re3.test(w)) { 117 | re = /.$/; 118 | w = w.replace(re,""); 119 | } 120 | else if (re4.test(w)) 121 | w = w + "e"; 122 | } 123 | } 124 | 125 | // Step 1c 126 | re = /^(.+?)y$/; 127 | if (re.test(w)) { 128 | var fp = re.exec(w); 129 | stem = fp[1]; 130 | re = new RegExp(s_v); 131 | if (re.test(stem)) 132 | w = stem + "i"; 133 | } 134 | 135 | // Step 2 136 | re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/; 137 | if (re.test(w)) { 138 | var fp = re.exec(w); 139 | stem = fp[1]; 140 | suffix = fp[2]; 141 | re = new RegExp(mgr0); 142 | if (re.test(stem)) 143 | w = stem + step2list[suffix]; 144 | } 145 | 146 | // Step 3 147 | re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/; 148 | if (re.test(w)) { 149 | var fp = re.exec(w); 150 | stem = fp[1]; 151 | suffix = fp[2]; 152 | re = new RegExp(mgr0); 153 | if (re.test(stem)) 154 | w = stem + step3list[suffix]; 155 | } 156 | 157 | // Step 4 158 | re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/; 159 | re2 = /^(.+?)(s|t)(ion)$/; 160 | if (re.test(w)) { 161 | var fp = re.exec(w); 162 | stem = fp[1]; 163 | re = new RegExp(mgr1); 164 | if (re.test(stem)) 165 | w = stem; 166 | } 167 | else if (re2.test(w)) { 168 | var fp = re2.exec(w); 169 | stem = fp[1] + fp[2]; 170 | re2 = new RegExp(mgr1); 171 | if (re2.test(stem)) 172 | w = stem; 173 | } 174 | 175 | // Step 5 176 | re = /^(.+?)e$/; 177 | if (re.test(w)) { 178 | var fp = re.exec(w); 179 | stem = fp[1]; 180 | re = new RegExp(mgr1); 181 | re2 = new RegExp(meq1); 182 | re3 = new RegExp("^" + C + v + "[^aeiouwxy]$"); 183 | if (re.test(stem) || (re2.test(stem) && !(re3.test(stem)))) 184 | w = stem; 185 | } 186 | re = /ll$/; 187 | re2 = new RegExp(mgr1); 188 | if (re.test(w) && re2.test(w)) { 189 | re = /.$/; 190 | w = w.replace(re,""); 191 | } 192 | 193 | // and turn initial Y back to y 194 | if (firstch == "y") 195 | w = firstch.toLowerCase() + w.substr(1); 196 | return w; 197 | } 198 | } 199 | 200 | -------------------------------------------------------------------------------- /docs/_build/html/_static/minus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/khughitt/labnote/0c7815936f955f3609628f8a5cab28b6ed2451c4/docs/_build/html/_static/minus.png -------------------------------------------------------------------------------- /docs/_build/html/_static/plus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/khughitt/labnote/0c7815936f955f3609628f8a5cab28b6ed2451c4/docs/_build/html/_static/plus.png -------------------------------------------------------------------------------- /docs/_build/html/_static/pygments.css: -------------------------------------------------------------------------------- 1 | pre { line-height: 125%; } 2 | td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } 3 | span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } 4 | td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } 5 | span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } 6 | .highlight .hll { background-color: #ffffcc } 7 | .highlight { background: #eeffcc; } 8 | .highlight .c { color: #408090; font-style: italic } /* Comment */ 9 | .highlight .err { border: 1px solid #FF0000 } /* Error */ 10 | .highlight .k { color: #007020; font-weight: bold } /* Keyword */ 11 | .highlight .o { color: #666666 } /* Operator */ 12 | .highlight .ch { color: #408090; font-style: italic } /* Comment.Hashbang */ 13 | .highlight .cm { color: #408090; font-style: italic } /* Comment.Multiline */ 14 | .highlight .cp { color: #007020 } /* Comment.Preproc */ 15 | .highlight .cpf { color: #408090; font-style: italic } /* Comment.PreprocFile */ 16 | .highlight .c1 { color: #408090; font-style: italic } /* Comment.Single */ 17 | .highlight .cs { color: #408090; background-color: #fff0f0 } /* Comment.Special */ 18 | .highlight .gd { color: #A00000 } /* Generic.Deleted */ 19 | .highlight .ge { font-style: italic } /* Generic.Emph */ 20 | .highlight .gr { color: #FF0000 } /* Generic.Error */ 21 | .highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ 22 | .highlight .gi { color: #00A000 } /* Generic.Inserted */ 23 | .highlight .go { color: #333333 } /* Generic.Output */ 24 | .highlight .gp { color: #c65d09; font-weight: bold } /* Generic.Prompt */ 25 | .highlight .gs { font-weight: bold } /* Generic.Strong */ 26 | .highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ 27 | .highlight .gt { color: #0044DD } /* Generic.Traceback */ 28 | .highlight .kc { color: #007020; font-weight: bold } /* Keyword.Constant */ 29 | .highlight .kd { color: #007020; font-weight: bold } /* Keyword.Declaration */ 30 | .highlight .kn { color: #007020; font-weight: bold } /* Keyword.Namespace */ 31 | .highlight .kp { color: #007020 } /* Keyword.Pseudo */ 32 | .highlight .kr { color: #007020; font-weight: bold } /* Keyword.Reserved */ 33 | .highlight .kt { color: #902000 } /* Keyword.Type */ 34 | .highlight .m { color: #208050 } /* Literal.Number */ 35 | .highlight .s { color: #4070a0 } /* Literal.String */ 36 | .highlight .na { color: #4070a0 } /* Name.Attribute */ 37 | .highlight .nb { color: #007020 } /* Name.Builtin */ 38 | .highlight .nc { color: #0e84b5; font-weight: bold } /* Name.Class */ 39 | .highlight .no { color: #60add5 } /* Name.Constant */ 40 | .highlight .nd { color: #555555; font-weight: bold } /* Name.Decorator */ 41 | .highlight .ni { color: #d55537; font-weight: bold } /* Name.Entity */ 42 | .highlight .ne { color: #007020 } /* Name.Exception */ 43 | .highlight .nf { color: #06287e } /* Name.Function */ 44 | .highlight .nl { color: #002070; font-weight: bold } /* Name.Label */ 45 | .highlight .nn { color: #0e84b5; font-weight: bold } /* Name.Namespace */ 46 | .highlight .nt { color: #062873; font-weight: bold } /* Name.Tag */ 47 | .highlight .nv { color: #bb60d5 } /* Name.Variable */ 48 | .highlight .ow { color: #007020; font-weight: bold } /* Operator.Word */ 49 | .highlight .w { color: #bbbbbb } /* Text.Whitespace */ 50 | .highlight .mb { color: #208050 } /* Literal.Number.Bin */ 51 | .highlight .mf { color: #208050 } /* Literal.Number.Float */ 52 | .highlight .mh { color: #208050 } /* Literal.Number.Hex */ 53 | .highlight .mi { color: #208050 } /* Literal.Number.Integer */ 54 | .highlight .mo { color: #208050 } /* Literal.Number.Oct */ 55 | .highlight .sa { color: #4070a0 } /* Literal.String.Affix */ 56 | .highlight .sb { color: #4070a0 } /* Literal.String.Backtick */ 57 | .highlight .sc { color: #4070a0 } /* Literal.String.Char */ 58 | .highlight .dl { color: #4070a0 } /* Literal.String.Delimiter */ 59 | .highlight .sd { color: #4070a0; font-style: italic } /* Literal.String.Doc */ 60 | .highlight .s2 { color: #4070a0 } /* Literal.String.Double */ 61 | .highlight .se { color: #4070a0; font-weight: bold } /* Literal.String.Escape */ 62 | .highlight .sh { color: #4070a0 } /* Literal.String.Heredoc */ 63 | .highlight .si { color: #70a0d0; font-style: italic } /* Literal.String.Interpol */ 64 | .highlight .sx { color: #c65d09 } /* Literal.String.Other */ 65 | .highlight .sr { color: #235388 } /* Literal.String.Regex */ 66 | .highlight .s1 { color: #4070a0 } /* Literal.String.Single */ 67 | .highlight .ss { color: #517918 } /* Literal.String.Symbol */ 68 | .highlight .bp { color: #007020 } /* Name.Builtin.Pseudo */ 69 | .highlight .fm { color: #06287e } /* Name.Function.Magic */ 70 | .highlight .vc { color: #bb60d5 } /* Name.Variable.Class */ 71 | .highlight .vg { color: #bb60d5 } /* Name.Variable.Global */ 72 | .highlight .vi { color: #bb60d5 } /* Name.Variable.Instance */ 73 | .highlight .vm { color: #bb60d5 } /* Name.Variable.Magic */ 74 | .highlight .il { color: #208050 } /* Literal.Number.Integer.Long */ -------------------------------------------------------------------------------- /docs/_build/html/_static/sphinx_highlight.js: -------------------------------------------------------------------------------- 1 | /* Highlighting utilities for Sphinx HTML documentation. */ 2 | "use strict"; 3 | 4 | const SPHINX_HIGHLIGHT_ENABLED = true 5 | 6 | /** 7 | * highlight a given string on a node by wrapping it in 8 | * span elements with the given class name. 9 | */ 10 | const _highlight = (node, addItems, text, className) => { 11 | if (node.nodeType === Node.TEXT_NODE) { 12 | const val = node.nodeValue; 13 | const parent = node.parentNode; 14 | const pos = val.toLowerCase().indexOf(text); 15 | if ( 16 | pos >= 0 && 17 | !parent.classList.contains(className) && 18 | !parent.classList.contains("nohighlight") 19 | ) { 20 | let span; 21 | 22 | const closestNode = parent.closest("body, svg, foreignObject"); 23 | const isInSVG = closestNode && closestNode.matches("svg"); 24 | if (isInSVG) { 25 | span = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); 26 | } else { 27 | span = document.createElement("span"); 28 | span.classList.add(className); 29 | } 30 | 31 | span.appendChild(document.createTextNode(val.substr(pos, text.length))); 32 | parent.insertBefore( 33 | span, 34 | parent.insertBefore( 35 | document.createTextNode(val.substr(pos + text.length)), 36 | node.nextSibling 37 | ) 38 | ); 39 | node.nodeValue = val.substr(0, pos); 40 | 41 | if (isInSVG) { 42 | const rect = document.createElementNS( 43 | "http://www.w3.org/2000/svg", 44 | "rect" 45 | ); 46 | const bbox = parent.getBBox(); 47 | rect.x.baseVal.value = bbox.x; 48 | rect.y.baseVal.value = bbox.y; 49 | rect.width.baseVal.value = bbox.width; 50 | rect.height.baseVal.value = bbox.height; 51 | rect.setAttribute("class", className); 52 | addItems.push({ parent: parent, target: rect }); 53 | } 54 | } 55 | } else if (node.matches && !node.matches("button, select, textarea")) { 56 | node.childNodes.forEach((el) => _highlight(el, addItems, text, className)); 57 | } 58 | }; 59 | const _highlightText = (thisNode, text, className) => { 60 | let addItems = []; 61 | _highlight(thisNode, addItems, text, className); 62 | addItems.forEach((obj) => 63 | obj.parent.insertAdjacentElement("beforebegin", obj.target) 64 | ); 65 | }; 66 | 67 | /** 68 | * Small JavaScript module for the documentation. 69 | */ 70 | const SphinxHighlight = { 71 | 72 | /** 73 | * highlight the search words provided in localstorage in the text 74 | */ 75 | highlightSearchWords: () => { 76 | if (!SPHINX_HIGHLIGHT_ENABLED) return; // bail if no highlight 77 | 78 | // get and clear terms from localstorage 79 | const url = new URL(window.location); 80 | const highlight = 81 | localStorage.getItem("sphinx_highlight_terms") 82 | || url.searchParams.get("highlight") 83 | || ""; 84 | localStorage.removeItem("sphinx_highlight_terms") 85 | url.searchParams.delete("highlight"); 86 | window.history.replaceState({}, "", url); 87 | 88 | // get individual terms from highlight string 89 | const terms = highlight.toLowerCase().split(/\s+/).filter(x => x); 90 | if (terms.length === 0) return; // nothing to do 91 | 92 | // There should never be more than one element matching "div.body" 93 | const divBody = document.querySelectorAll("div.body"); 94 | const body = divBody.length ? divBody[0] : document.querySelector("body"); 95 | window.setTimeout(() => { 96 | terms.forEach((term) => _highlightText(body, term, "highlighted")); 97 | }, 10); 98 | 99 | const searchBox = document.getElementById("searchbox"); 100 | if (searchBox === null) return; 101 | searchBox.appendChild( 102 | document 103 | .createRange() 104 | .createContextualFragment( 105 | '<p class="highlight-link">' + 106 | '<a href="javascript:SphinxHighlight.hideSearchWords()">' + 107 | _("Hide Search Matches") + 108 | "</a></p>" 109 | ) 110 | ); 111 | }, 112 | 113 | /** 114 | * helper function to hide the search marks again 115 | */ 116 | hideSearchWords: () => { 117 | document 118 | .querySelectorAll("#searchbox .highlight-link") 119 | .forEach((el) => el.remove()); 120 | document 121 | .querySelectorAll("span.highlighted") 122 | .forEach((el) => el.classList.remove("highlighted")); 123 | localStorage.removeItem("sphinx_highlight_terms") 124 | }, 125 | 126 | initEscapeListener: () => { 127 | // only install a listener if it is really needed 128 | if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) return; 129 | 130 | document.addEventListener("keydown", (event) => { 131 | // bail for input elements 132 | if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return; 133 | // bail with special keys 134 | if (event.shiftKey || event.altKey || event.ctrlKey || event.metaKey) return; 135 | if (DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS && (event.key === "Escape")) { 136 | SphinxHighlight.hideSearchWords(); 137 | event.preventDefault(); 138 | } 139 | }); 140 | }, 141 | }; 142 | 143 | _ready(SphinxHighlight.highlightSearchWords); 144 | _ready(SphinxHighlight.initEscapeListener); 145 | -------------------------------------------------------------------------------- /docs/_build/html/objects.inv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/khughitt/labnote/0c7815936f955f3609628f8a5cab28b6ed2451c4/docs/_build/html/objects.inv -------------------------------------------------------------------------------- /docs/_build/html/searchindex.js: -------------------------------------------------------------------------------- 1 | Search.setIndex({"docnames": ["configuration", "development", "features", "index", "installation"], "filenames": ["configuration.rst", "development.rst", "features.rst", "index.rst", "installation.rst"], "titles": ["Configuration", "Development", "Features", "Welcome to Labnote\u2019s documentation!", "Installation"], "terms": {"labnot": [0, 1, 2], "set": [0, 3, 4], "can": [0, 1, 2, 3, 4], "specifi": [0, 2, 3], "either": [0, 2], "via": 0, "command": [0, 1, 4], "line": [0, 2, 4], "run": [0, 2, 3, 4], "time": [0, 2], "e": [0, 1, 3, 4], "g": [0, 3, 4], "i": [0, 1, 2, 3, 4], "some": [0, 3], "path": [0, 2, 4], "o": [0, 4], "dir": [0, 4], "us": [0, 1, 2, 3, 4], "yaml": [0, 2], "config": [0, 2, 4], "both": 0, "By": [0, 3], "default": [0, 3, 4], "look": [0, 4], "name": [0, 2, 4], "yml": [0, 2, 4], "locat": [0, 4], "home": [0, 4], "If": [0, 1, 4], "thi": [0, 1, 2, 3, 4], "exist": [0, 3, 4], "": [0, 4], "behavior": [0, 2], "The": [0, 1, 4], "should": [0, 2, 4], "someth": [0, 3, 4], "like": [0, 1, 3, 4], "inform": [0, 2, 4], "titl": [0, 2], "lab": [0, 2, 3, 4], "notebook": [0, 3], "author": 0, "your": [0, 1, 2, 3, 4], "email": 0, "address": 0, "com": [0, 1, 2, 4], "content": [0, 3], "input_dir": [0, 4], "user": [0, 2, 4], "dropbox": 0, "research": [0, 2, 4], "201": 0, "2": 0, "5": 0, "2016": 0, "output_fil": [0, 4], "index": [0, 3, 4], "html": [0, 3, 4], "include_fil": 0, "py": [0, 1, 3, 4], "ipynb": 0, "readm": [0, 2, 3, 4], "sequenc": 0, "analysi": [0, 2, 3], "seq": 0, "dna": 0, "rna": 0, "differenti": 0, "express": 0, "dea": 0, "network": 0, "visual": 0, "viz": 0, "main": 0, "ar": [0, 1, 2, 3, 4], "import": 0, "defin": [0, 3], "One": 0, "more": [0, 1, 2, 3, 4], "wildcard": 0, "filepath": [0, 4], "save": [0, 2, 4], "result": [0, 2, 3], "its": 0, "associ": 0, "most": [0, 4], "often": 0, "parent": 0, "directori": [0, 1, 2, 3, 4], "input": [0, 4], "possibli": 0, "web": [0, 2], "access": 0, "var": 0, "www": [0, 2], "public_html": 0, "link": [0, 2], "A": [0, 4], "you": [0, 1, 2, 3, 4], "would": [0, 1, 4], "organis": 0, "along": 0, "search": 0, "string": 0, "which": [0, 2, 4], "find": 0, "project": [0, 3], "place": 0, "under": 0, "those": 0, "also": [0, 2], "point": [0, 3], "differ": [0, 1, 3], "c": [0, 4], "switch": 0, "take": 0, "preced": 0, "depend": [0, 4], "how": [0, 3, 4], "have": [0, 1, 4], "organ": [0, 3], "mai": [0, 2], "difficult": 0, "setup": [0, 4], "It": [0, 2, 3], "work": [0, 2, 3, 4], "best": 0, "normal": [0, 2], "relat": [0, 2, 3], "analys": [0, 3], "all": [0, 4], "similar": [0, 1], "compon": 0, "xx": 0, "possibl": [0, 3], "conveni": 0, "support": [0, 3, 4], "manual": 0, "categor": 0, "hidden": 0, "metafil": 0, "insid": 0, "each": [0, 2, 3, 4], "print": [0, 4], "an": [0, 1, 2, 3], "empti": 0, "custom": [0, 3, 4], "mkdir": 0, "p": 0, "below": 0, "complet": 0, "list": [0, 4], "thei": 0, "expect": 0, "except": 0, "paramet": 0, "contact": [0, 1], "one": [0, 3], "absolut": 0, "rel": [0, 2], "scan": [0, 3, 4], "correspond": 0, "entri": [0, 3, 4], "onli": [0, 3], "filenam": 0, "match": [0, 3], "pattern": [0, 3], "describ": [0, 3], "ad": [0, 1, 2, 4], "exclud": 0, "indic": 0, "ani": [0, 2], "part": 0, "skip": [0, 1], "extern": [0, 2], "dictionari": 0, "contain": [0, 1, 2, 4], "must": [0, 4], "url": [0, 4], "kei": 0, "where": [0, 1, 2], "exampl": [0, 1, 2, 4], "interact": 0, "shini": 0, "http": [0, 1, 2, 4], "shinyapp": 0, "io": 0, "seri": 0, "viewer": 0, "server": 0, "3838": 0, "group": [0, 2], "For": [0, 1, 2, 4], "when": [0, 4], "compar": 0, "against": 0, "assign": [0, 2], "first": [0, 1, 2], "els": 0, "other": 0, "In": [0, 2, 3], "addit": [0, 2], "imag": [0, 2, 4], "descript": [0, 2], "field": [0, 2], "theme": [0, 3], "dure": 0, "edg": 0, "between": 0, "topologi": 0, "diffexpr": 0, "resourc": [0, 2, 3, 4], "img": [0, 4], "maplot": 0, "jpg": [0, 4], "regulatori": 0, "element": [0, 2], "detect": [0, 2], "motif": 0, "elem": 0, "random": 0, "forest": 0, "2015": 0, "15": 0, "gene": 0, "reg": 0, "motif_exampl": 0, "png": 0, "abov": [0, 1], "membership": 0, "determin": [0, 2], "next": 0, "two": [0, 2], "render": [0, 2], "current": [0, 2, 3, 4], "provid": [0, 2, 3], "sort_categories_by_d": 0, "boolean": 0, "whether": 0, "sort": [0, 3], "date": [0, 3, 4], "recent": [0, 2, 4], "within": [0, 1], "true": 0, "sort_entries_by_d": 0, "decreas": 0, "order": [0, 3], "last": [0, 3], "modifi": [0, 3], "otherwis": [0, 4], "alphanumer": 0, "fals": 0, "written": 0, "css": [0, 4], "etc": [0, 2], "copi": 0, "url_prefix": [0, 4], "prefix": [0, 4], "prepend": 0, "remot": 0, "host": 0, "version": [0, 1, 2, 4], "user_css": [0, 2, 4], "user_j": [0, 2, 4], "javascript": [0, 2, 4], "There": 1, "few": [1, 3], "wai": [1, 4], "easiest": [1, 4], "simpli": [1, 3, 4], "fork": 1, "repo": 1, "add": [1, 4], "whatev": [1, 4], "fix": 1, "function": [1, 2], "submit": 1, "pull": 1, "request": 1, "chang": [1, 2], "pleas": 1, "sure": 1, "write": 1, "unit": 1, "relev": 1, "idea": 1, "suggest": 1, "aren": 1, "t": [1, 2], "abl": [1, 2], "implement": 1, "them": 1, "yourself": 1, "feel": 1, "free": 1, "me": 1, "open": 1, "up": [1, 4], "issu": 1, "github": [1, 4], "virtualenv": 1, "from": [1, 3, 4], "virtualenvwrapp": 1, "git": [1, 4], "clone": 1, "khughitt": [1, 4], "cd": [1, 4], "mkvirtualenv": 1, "pip": [1, 4], "instal": [1, 3], "pytest": 1, "hash": 1, "r": 1, "alreadi": 1, "step": 1, "need": 1, "after": 1, "ensur": 1, "system": [1, 3, 4], "To": [1, 3, 4], "python": [1, 4], "second": 1, "repeat": 1, "process": [1, 2], "python3": [1, 2], "3": [1, 2, 4], "labnote33": 1, "note": [1, 2, 4], "base": [1, 3], "pretti": 1, "later": 1, "while": [2, 3], "capabl": 2, "includ": 2, "arbitrari": 2, "type": [2, 3], "sever": 2, "nativ": 2, "recogn": 2, "extend": 2, "automat": [2, 4], "futur": 2, "real": 2, "convers": 2, "attempt": [2, 3], "choos": 2, "appropri": 2, "docstr": 2, "jupyt": 2, "metadata": 2, "found": 2, "edit": [2, 4], "json": 2, "sourc": [2, 4], "section": [2, 4], "block": 2, "kernelspec": 2, "display_nam": 2, "languag": 2, "built": 2, "editor": 2, "although": 2, "plain": 2, "text": 2, "vim": 2, "fine": 2, "appear": 2, "see": [2, 4], "configur": [2, 3, 4], "specif": 2, "control": 2, "These": 2, "follow": [2, 4], "pipelin": 2, "sh": 2, "my": 2, "interest": 2, "furthermor": 2, "page": 2, "present": 2, "slide": 2, "googl": 2, "extra": 2, "ppt": 2, "ship": 2, "singl": [2, 3], "creativ": 2, "shown": [2, 4], "screenshot": [2, 4], "skeleton": 2, "framework": 2, "produc": 2, "simpl": 2, "three": 2, "column": 2, "layout": 2, "center": 2, "option": [2, 3, 4], "side": 2, "welcom": 2, "contribut": [2, 3], "own": [2, 4], "develop": [2, 3], "guid": 2, "creat": [2, 3, 4], "new": [2, 3], "allow": [2, 3], "regardless": 2, "being": 2, "output": [2, 3, 4], "want": 2, "font": 2, "header": 2, "could": 2, "famili": 2, "helvetica": 2, "san": 2, "serif": 2, "so": [2, 4], "regener": [2, 4], "now": [2, 4], "instead": 2, "cursiv": 2, "flexibl": 3, "lightweight": 3, "tool": 3, "gener": 3, "electron": 3, "rather": 3, "than": 3, "unifi": 3, "share": 3, "ti": 3, "togeth": 3, "build": 3, "short": 3, "help": [3, 4], "go": 3, "animal_behavior": [3, 4], "molothru": [3, 4], "barnacl": [3, 4], "cirriped": [3, 4], "morphologi": [3, 4], "taxonomi": [3, 4], "finch": [3, 4], "beak": [3, 4], "size": [3, 4], "comparison": [3, 4], "beak_siz": [3, 4], "forag": [3, 4], "strategi": [3, 4], "natur": [3, 4], "select": [3, 4], "thought": [3, 4], "txt": [3, 4], "file": [3, 4], "pertain": 3, "log": 3, "script": 3, "particular": 3, "construct": 3, "tabl": 3, "categori": 3, "separ": 3, "topic": 3, "mani": 3, "style": 3, "tri": 3, "make": 3, "assumpt": 3, "about": 3, "wide": 3, "rang": 3, "structur": 3, "final": 3, "design": 3, "extens": 3, "frill": 3, "jinga2": 3, "templat": 3, "trivial": 3, "requir": 3, "test": 3, "autom": 3, "featur": 3, "format": 3, "individu": 3, "avail": 4, "machin": 4, "addition": 4, "librari": 4, "beauti": 4, "soup": 4, "4": 4, "jinja2": 4, "pyyaml": 4, "aim": 4, "window": 4, "linux": 4, "x": 4, "latest": 4, "stabl": 4, "full": 4, "h": 4, "usag": 4, "u": 4, "j": 4, "argument": 4, "show": 4, "messag": 4, "exit": 4, "Will": 4, "screen": 4, "stylesheet": 4, "start": 4, "load": 4, "1854_balanidae_f339": 4, "2_figlbp12": 4, "a1417007h": 4, "privat": 4, "finish": 4, "what": 4, "overview": 4, "document": 4, "readi": 4, "keep": 4, "everi": 4, "dai": 4, "easili": 4, "cron": 4, "onc": 4, "crontab": 4, "level": 4, "job": 4, "daili": 4, "call": 4, "cronjob": 4, "ubuntu": 4, "tutori": 4}, "objects": {}, "objtypes": {}, "objnames": {}, "titleterms": {"configur": 0, "overview": [0, 3], "option": 0, "gener": [0, 4], "file": [0, 2], "includ": 0, "categori": [0, 2], "appear": 0, "output": 0, "develop": 1, "contribut": 1, "run": 1, "test": [1, 4], "creat": 1, "new": 1, "theme": [1, 2], "featur": 2, "support": 2, "format": 2, "html": 2, "python": 2, "ipython": 2, "notebook": [2, 4], "custom": 2, "individu": 2, "entri": 2, "default": 2, "css": 2, "j": 2, "welcom": 3, "labnot": [3, 4], "": 3, "document": 3, "instal": 4, "requir": 4, "autom": 4}, "envversion": {"sphinx.domains.c": 2, "sphinx.domains.changeset": 1, "sphinx.domains.citation": 1, "sphinx.domains.cpp": 8, "sphinx.domains.index": 1, "sphinx.domains.javascript": 2, "sphinx.domains.math": 2, "sphinx.domains.python": 3, "sphinx.domains.rst": 2, "sphinx.domains.std": 2, "sphinx": 57}, "alltitles": {"Configuration": [[0, "configuration"]], "Overview": [[0, "overview"], [3, "overview"]], "Configuration Options": [[0, "configuration-options"]], "General options": [[0, "general-options"]], "Files to include": [[0, "files-to-include"]], "Categories": [[0, "categories"]], "Appearance": [[0, "appearance"]], "Output": [[0, "output"]], "Development": [[1, "development"]], "Contributing": [[1, "contributing"]], "Running tests": [[1, "running-tests"]], "Creating new themes": [[1, "creating-new-themes"]], "Features": [[2, "features"]], "Supported file formats": [[2, "supported-file-formats"]], "HTML": [[2, "html"]], "Python": [[2, "python"]], "IPython Notebook": [[2, "ipython-notebook"]], "Customizing categories": [[2, "customizing-categories"]], "Customizing individual entries": [[2, "customizing-individual-entries"]], "Themes": [[2, "themes"]], "Default theme": [[2, "default-theme"]], "Customizing themes with CSS/JS": [[2, "customizing-themes-with-css-js"]], "Welcome to Labnote\u2019s documentation!": [[3, "welcome-to-labnote-s-documentation"]], "Documentation": [[3, "documentation"]], "Installation": [[4, "installation"]], "Requirements": [[4, "requirements"]], "Installing labnote": [[4, "installing-labnote"]], "Testing installation": [[4, "testing-installation"]], "Automating notebook generation": [[4, "automating-notebook-generation"]]}, "indexentries": {}}) -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Labnote documentation build configuration file, created by 5 | # sphinx-quickstart on Sun Mar 13 16:38:22 2016. 6 | # 7 | # This file is execfile()d with the current directory set to its 8 | # containing dir. 9 | # 10 | # Note that not all possible configuration values are present in this 11 | # autogenerated file. 12 | # 13 | # All configuration values have a default; values that are commented out 14 | # serve to show the default. 15 | 16 | import sys 17 | import os 18 | 19 | # If extensions (or modules to document with autodoc) are in another directory, 20 | # add these directories to sys.path here. If the directory is relative to the 21 | # documentation root, use os.path.abspath to make it absolute, like shown here. 22 | #sys.path.insert(0, os.path.abspath('.')) 23 | 24 | # -- General configuration ------------------------------------------------ 25 | 26 | # If your documentation needs a minimal Sphinx version, state it here. 27 | #needs_sphinx = '1.0' 28 | 29 | # Add any Sphinx extension module names here, as strings. They can be 30 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 31 | # ones. 32 | extensions = [] 33 | 34 | # Add any paths that contain templates here, relative to this directory. 35 | templates_path = ['_templates'] 36 | 37 | # The suffix(es) of source filenames. 38 | # You can specify multiple suffix as a list of string: 39 | # source_suffix = ['.rst', '.md'] 40 | source_suffix = '.rst' 41 | 42 | # The encoding of source files. 43 | #source_encoding = 'utf-8-sig' 44 | 45 | # The master toctree document. 46 | master_doc = 'index' 47 | 48 | # General information about the project. 49 | project = 'Labnote' 50 | copyright = '2016, Keith Hughitt' 51 | author = 'Keith Hughitt' 52 | 53 | # The version info for the project you're documenting, acts as replacement for 54 | # |version| and |release|, also used in various other places throughout the 55 | # built documents. 56 | # 57 | # The short X.Y version. 58 | version = '0.9' 59 | # The full version, including alpha/beta/rc tags. 60 | release = '0.9' 61 | 62 | # The language for content autogenerated by Sphinx. Refer to documentation 63 | # for a list of supported languages. 64 | # 65 | # This is also used if you do content translation via gettext catalogs. 66 | # Usually you set "language" from the command line for these cases. 67 | language = None 68 | 69 | # There are two options for replacing |today|: either, you set today to some 70 | # non-false value, then it is used: 71 | #today = '' 72 | # Else, today_fmt is used as the format for a strftime call. 73 | #today_fmt = '%B %d, %Y' 74 | 75 | # List of patterns, relative to source directory, that match files and 76 | # directories to ignore when looking for source files. 77 | exclude_patterns = ['_build'] 78 | 79 | # The reST default role (used for this markup: `text`) to use for all 80 | # documents. 81 | #default_role = None 82 | 83 | # If true, '()' will be appended to :func: etc. cross-reference text. 84 | #add_function_parentheses = True 85 | 86 | # If true, the current module name will be prepended to all description 87 | # unit titles (such as .. function::). 88 | #add_module_names = True 89 | 90 | # If true, sectionauthor and moduleauthor directives will be shown in the 91 | # output. They are ignored by default. 92 | #show_authors = False 93 | 94 | # The name of the Pygments (syntax highlighting) style to use. 95 | pygments_style = 'sphinx' 96 | 97 | # A list of ignored prefixes for module index sorting. 98 | #modindex_common_prefix = [] 99 | 100 | # If true, keep warnings as "system message" paragraphs in the built documents. 101 | #keep_warnings = False 102 | 103 | # If true, `todo` and `todoList` produce output, else they produce nothing. 104 | todo_include_todos = False 105 | 106 | 107 | # -- Options for HTML output ---------------------------------------------- 108 | 109 | # The theme to use for HTML and HTML Help pages. See the documentation for 110 | # a list of builtin themes. 111 | html_theme = 'alabaster' 112 | 113 | # Theme options are theme-specific and customize the look and feel of a theme 114 | # further. For a list of options available for each theme, see the 115 | # documentation. 116 | #html_theme_options = {} 117 | 118 | # Add any paths that contain custom themes here, relative to this directory. 119 | #html_theme_path = [] 120 | 121 | # The name for this set of Sphinx documents. If None, it defaults to 122 | # "<project> v<release> documentation". 123 | #html_title = None 124 | 125 | # A shorter title for the navigation bar. Default is the same as html_title. 126 | #html_short_title = None 127 | 128 | # The name of an image file (relative to this directory) to place at the top 129 | # of the sidebar. 130 | #html_logo = None 131 | 132 | # The name of an image file (relative to this directory) to use as a favicon of 133 | # the docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 134 | # pixels large. 135 | #html_favicon = None 136 | 137 | # Add any paths that contain custom static files (such as style sheets) here, 138 | # relative to this directory. They are copied after the builtin static files, 139 | # so a file named "default.css" will overwrite the builtin "default.css". 140 | html_static_path = ['_static'] 141 | 142 | # Add any extra paths that contain custom files (such as robots.txt or 143 | # .htaccess) here, relative to this directory. These files are copied 144 | # directly to the root of the documentation. 145 | #html_extra_path = [] 146 | 147 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, 148 | # using the given strftime format. 149 | #html_last_updated_fmt = '%b %d, %Y' 150 | 151 | # If true, SmartyPants will be used to convert quotes and dashes to 152 | # typographically correct entities. 153 | #html_use_smartypants = True 154 | 155 | # Custom sidebar templates, maps document names to template names. 156 | #html_sidebars = {} 157 | html_sidebars = { '**': ['globaltoc.html', 'relations.html', 'sourcelink.html', 'searchbox.html'], } 158 | 159 | # Additional templates that should be rendered to pages, maps page names to 160 | # template names. 161 | #html_additional_pages = {} 162 | 163 | # If false, no module index is generated. 164 | #html_domain_indices = True 165 | 166 | # If false, no index is generated. 167 | #html_use_index = True 168 | 169 | # If true, the index is split into individual pages for each letter. 170 | #html_split_index = False 171 | 172 | # If true, links to the reST sources are added to the pages. 173 | #html_show_sourcelink = True 174 | 175 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. 176 | #html_show_sphinx = True 177 | 178 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. 179 | #html_show_copyright = True 180 | 181 | # If true, an OpenSearch description file will be output, and all pages will 182 | # contain a <link> tag referring to it. The value of this option must be the 183 | # base URL from which the finished HTML is served. 184 | #html_use_opensearch = '' 185 | 186 | # This is the file name suffix for HTML files (e.g. ".xhtml"). 187 | #html_file_suffix = None 188 | 189 | # Language to be used for generating the HTML full-text search index. 190 | # Sphinx supports the following languages: 191 | # 'da', 'de', 'en', 'es', 'fi', 'fr', 'h', 'it', 'ja' 192 | # 'nl', 'no', 'pt', 'ro', 'r', 'sv', 'tr' 193 | #html_search_language = 'en' 194 | 195 | # A dictionary with options for the search language support, empty by default. 196 | # Now only 'ja' uses this config value 197 | #html_search_options = {'type': 'default'} 198 | 199 | # The name of a javascript file (relative to the configuration directory) that 200 | # implements a search results scorer. If empty, the default will be used. 201 | #html_search_scorer = 'scorer.js' 202 | 203 | # Output file base name for HTML help builder. 204 | htmlhelp_basename = 'Labnotedoc' 205 | 206 | # -- Options for LaTeX output --------------------------------------------- 207 | 208 | latex_elements = { 209 | # The paper size ('letterpaper' or 'a4paper'). 210 | #'papersize': 'letterpaper', 211 | 212 | # The font size ('10pt', '11pt' or '12pt'). 213 | #'pointsize': '10pt', 214 | 215 | # Additional stuff for the LaTeX preamble. 216 | #'preamble': '', 217 | 218 | # Latex figure (float) alignment 219 | #'figure_align': 'htbp', 220 | } 221 | 222 | # Grouping the document tree into LaTeX files. List of tuples 223 | # (source start file, target name, title, 224 | # author, documentclass [howto, manual, or own class]). 225 | latex_documents = [ 226 | (master_doc, 'Labnote.tex', 'Labnote Documentation', 227 | 'Keith Hughitt', 'manual'), 228 | ] 229 | 230 | # The name of an image file (relative to this directory) to place at the top of 231 | # the title page. 232 | #latex_logo = None 233 | 234 | # For "manual" documents, if this is true, then toplevel headings are parts, 235 | # not chapters. 236 | #latex_use_parts = False 237 | 238 | # If true, show page references after internal links. 239 | #latex_show_pagerefs = False 240 | 241 | # If true, show URL addresses after external links. 242 | #latex_show_urls = False 243 | 244 | # Documents to append as an appendix to all manuals. 245 | #latex_appendices = [] 246 | 247 | # If false, no module index is generated. 248 | #latex_domain_indices = True 249 | 250 | 251 | # -- Options for manual page output --------------------------------------- 252 | 253 | # One entry per manual page. List of tuples 254 | # (source start file, name, description, authors, manual section). 255 | man_pages = [ 256 | (master_doc, 'labnote', 'Labnote Documentation', 257 | [author], 1) 258 | ] 259 | 260 | # If true, show URL addresses after external links. 261 | #man_show_urls = False 262 | 263 | 264 | # -- Options for Texinfo output ------------------------------------------- 265 | 266 | # Grouping the document tree into Texinfo files. List of tuples 267 | # (source start file, target name, title, author, 268 | # dir menu entry, description, category) 269 | texinfo_documents = [ 270 | (master_doc, 'Labnote', 'Labnote Documentation', 271 | author, 'Labnote', 'One line description of project.', 272 | 'Miscellaneous'), 273 | ] 274 | 275 | # Documents to append as an appendix to all manuals. 276 | #texinfo_appendices = [] 277 | 278 | # If false, no module index is generated. 279 | #texinfo_domain_indices = True 280 | 281 | # How to display URL addresses: 'footnote', 'no', or 'inline'. 282 | #texinfo_show_urls = 'footnote' 283 | 284 | # If true, do not generate a @detailmenu in the "Top" node's menu. 285 | #texinfo_no_detailmenu = False 286 | -------------------------------------------------------------------------------- /docs/configuration.rst: -------------------------------------------------------------------------------- 1 | .. _configuration: 2 | 3 | Configuration 4 | ============= 5 | 6 | Overview 7 | -------- 8 | 9 | Labnote settings can be specified either via the command-line at run-time (e.g. 10 | ``labnote -i /some/path/* -o /output/dir``), or using a `YAML 11 | <http://yaml.org/>`__ config file, or both. 12 | 13 | By default, Labnote looks for a file named ``config.yml`` located in 14 | ``$HOME/.config/labnote/``. If this file exists, then it will be used 15 | used to configure Labnote's behavior. 16 | 17 | The configuration file should look something like: 18 | 19 | .. code:: yaml 20 | 21 | --- 22 | # General information 23 | title: Lab Notebook 24 | author: Your Name 25 | email: email@address.com 26 | 27 | # Notebook contents 28 | input_dirs: 29 | - /home/user/Dropbox/research/201[2-5]/* 30 | - /home/user/Dropbox/research/2016/*/* 31 | 32 | output_file: /home/user/Dropbox/research/index.html 33 | 34 | include_files: ['*.html', '*.py', '*.ipynb', 'README.*'] 35 | 36 | # Research categories 37 | categories: 38 | 'Sequence Analysis': ['seq', 'dna', 'rna'] 39 | 'Differential Expression': ['dea', 'differential-expression'] 40 | 'Network Analysis': ['network'] 41 | 'Visualization': ['viz'] 42 | 43 | The main settings that are important to define are: 44 | 45 | 1. ``input_dirs`` - One or more 46 | `wildcard <http://tldp.org/LDP/GNU-Linux-Tools-Summary/html/x11655.htm>`__ 47 | filepath expressions 48 | 2. ``output_file`` - Path to save resulting HTML and its associated files 49 | to. Most often, this will be located some parent directory of the input 50 | directories, possibly in a web-accessible location (e.g. 51 | ``/var/www/index.html`` or ``~/public_html/notebook.html``). 52 | 3. ``include_files`` - Files to link to in your notebook. 53 | 4. ``categories`` - A set of categories you would like to use to 54 | organise your notebook, along with some search strings which can be 55 | used to find project directories that should be placed under those 56 | categories.\* 57 | 58 | You can also point to a config file located in a different location 59 | using the ``-c`` option, e.g. ``labnote -c /path/to/config.yml``. If a 60 | setting is specified both in a configuration file and using a 61 | command-line switch, the option specified on the command-line will take 62 | precedence. 63 | 64 | \*Depending on how you have organized your files, this may be difficult 65 | to setup. It works best if you can normalize your directory names such 66 | that related analyses all include a similar component (e.g. 67 | 'xx-network-analysis'). 68 | 69 | If that is not possible or convenient, Labnote also supports manually 70 | specifying a projects categorization using hidden `.labnote` metafiles inside 71 | each project directory. 72 | 73 | The ``--print-config`` option can be used to generate an empty config file 74 | which you can then customize to your liking, e.g.: 75 | 76 | :: 77 | 78 | mkdir -p $HOME/.config/labnote 79 | labnote --print-config > $HOME/.config/labnote/config.yml 80 | 81 | Configuration Options 82 | --------------------- 83 | 84 | Below is a complete list of Labnote configuration options and how they are 85 | expected to be used. 86 | 87 | Except for ``input_dirs`` and ``output_file``, all configuration parameters are 88 | optional. 89 | 90 | General options 91 | ~~~~~~~~~~~~~~~ 92 | 93 | - ``author`` Author name (string). 94 | - ``email`` Author contact email address (string). 95 | - ``title`` Lab notebook title (string). 96 | 97 | Files to include 98 | ~~~~~~~~~~~~~~~~ 99 | 100 | - ``input_dirs`` List of one or more absolute or relative directory paths which 101 | should be scanned for files corresponding to notebook entries. Only those 102 | filenames which match the patterns specified in the ``include_files`` option 103 | described below will be added. 104 | - ``include_files``: List of filename wildcard expressions specifying the files 105 | to be included in the notebook. 106 | - ``exclude`` List of strings indicating files which should not be included in 107 | the notebook. If any part of a file's directory or filename matches a string 108 | in the ``exclude`` list, it will be skipped. 109 | - ``external``: A dictionary containing one or more external links which should 110 | be included as notebook entries. Each external entry must include a ``url`` 111 | key indicating where the entry can be accessed, and, optionally, a 112 | ``category``. 113 | 114 | Example: 115 | 116 | .. code:: yaml 117 | 118 | external: 119 | 'Interactive network visualization': 120 | category: 'Shiny' 121 | url: 'http://user.shinyapps.io/network-viz' 122 | 'Interactive time series viewer': 123 | category: 'Shiny' 124 | url: 'http://server.com:3838/time-series-viewer' 125 | 126 | Categories 127 | ~~~~~~~~~~ 128 | 129 | - ``categories`` List of dictionaries describing how entries should be grouped. 130 | 131 | For each category, a list of one or more patterns should be specified. When 132 | entries are added to the notebook, the entry directory and filenames will be 133 | compared against the category patterns, and the entry will be assigned to 134 | the first category that it matches, or else default to an "Other" category. 135 | 136 | In addition to the entry search patterns, each category may also include an 137 | image and description field, which may be used by themes during the notebook 138 | HTML generation. 139 | 140 | Example: 141 | 142 | .. code:: yaml 143 | 144 | categories: 145 | 'Network Analysis': ['network', 'edge-betweenness', 'topology'] 146 | 'Differential Expression': 147 | patterns: ['diffexpr', 'dea'] 148 | image: 'resources/user/img/maplot.jpg' 149 | 'Regulatory Element Detection': 150 | patterns: ['motif', 'regulatory-elem', 'random-forest'] 151 | image: '2015/15-random-forest-gene-reg/output/motif_example.png' 152 | 153 | In the above example, for the first category, only the search patterns to be 154 | used for notebook entry membership determination are specified. The next two 155 | categories also specify and image to use. 156 | 157 | Appearance 158 | ~~~~~~~~~~ 159 | 160 | - ``theme`` String indicating the Labnote theme to be used for the rendered 161 | notebook. Currently there is only one theme provided: "default". 162 | - ``sort_categories_by_date`` Boolean indicating whether the categories should 163 | be sorted by the date of the most recent entry within each category. 164 | (default: true) 165 | - ``sort_entries_by_date``: Boolean indicating whether the entries within each 166 | category should be sorted by date. If true, entries within each category will 167 | appear in decreasing order by date last modified, otherwise they will be 168 | sorted alphanumerically. (default: false). 169 | 170 | Output 171 | ~~~~~~ 172 | 173 | - ``output_file``: Filepath where notebook should be written. Notebook resources 174 | (CSS, images, etc.) will also be copied to the directory containing the 175 | indicated output file. 176 | - ``url_prefix``: A string prefix to be prepended to each entry URL. This can be 177 | used, for example, to point to a remotely-hosted version of the notebook. 178 | - ``user_css``: Custom CSS file to include in the output HTML (string). 179 | - ``user_js``: Custom JavaScript file to include in the output HTML (string). 180 | 181 | -------------------------------------------------------------------------------- /docs/development.rst: -------------------------------------------------------------------------------- 1 | Development 2 | ----------- 3 | 4 | Contributing 5 | ~~~~~~~~~~~~ 6 | 7 | There are a few different ways you can contribute to the development of 8 | Labnote. The easiest way is to simply fork the Labnote repo, add whatever 9 | fixes or functionality you would like, and submit a pull request with the 10 | changes. 11 | 12 | If you are adding new functionality, please be sure to write unit tests where 13 | relevant. 14 | 15 | If you have ideas or suggestions, but aren't able to implement them yourself, 16 | feel free to `contact me <mailto:khughitt@umd.edu>`__, or open up an issue with 17 | your idea or suggestion on the `Labnote Github repo 18 | <https://github.com/khughitt/labnote/issues>`__. 19 | 20 | Running tests 21 | ~~~~~~~~~~~~~ 22 | 23 | The easiest way to run the unit tests for labnote is to create a 24 | virtualenv container and run the tests from within there. For example, 25 | if you have 26 | `virtualenvwrapper <https://virtualenvwrapper.readthedocs.org/en/latest/>`__, 27 | you can run: 28 | 29 | :: 30 | 31 | git clone https://github.com/khughitt/labnote && cd labnote 32 | mkvirtualenv labnote 33 | pip install -e . 34 | pip install pytest 35 | hash -r 36 | py.test 37 | 38 | If you already cloned the labnote repo, you can skip the first step 39 | above and simply ``cd`` to the repo directory. 40 | 41 | The ``hash -r`` command in the above is needed after installing py.test 42 | to ensure that the virtualenv version of py.test is used, and not a 43 | system version. 44 | 45 | To run the tests for a different version of Python, you can simply 46 | create a second virtualenv for that version of Python and repeat the 47 | process: 48 | 49 | :: 50 | 51 | mkvirtualenv --python=python3.3 labnote33 52 | 53 | Note that virtualenvwrapper is not needed to run the tests, and the 54 | commands for using the base version of virtualenv are pretty similar. 55 | 56 | Creating new themes 57 | ~~~~~~~~~~~~~~~~~~~ 58 | 59 | More on this later... 60 | 61 | 62 | -------------------------------------------------------------------------------- /docs/features.rst: -------------------------------------------------------------------------------- 1 | .. _features: 2 | 3 | Features 4 | -------- 5 | 6 | Supported file formats 7 | ~~~~~~~~~~~~~~~~~~~~~~ 8 | 9 | While labnote is capable of including arbitrary file types, there are several 10 | file types which Labnote natively recognizes and is able to provide extended 11 | functionality for such as automatic title determination, and (in the future), 12 | real-time conversion. For each of these, Labnote will attempt to automatically choose an appropriate 13 | title. 14 | 15 | HTML 16 | '''' 17 | 18 | For HTML files, the `<title>` element will be used. 19 | 20 | Python 21 | '''''' 22 | 23 | For Python files, the first line of a file docstring will be used as a title. 24 | 25 | IPython Notebook 26 | '''''''''''''''' 27 | 28 | For IPython (Jupyter) notebooks, a custom `metadata` field will be used if 29 | found. This can be added by editing the JSON source for the notebook, and 30 | adding a ``labnote`` section to the ``metadata`` JSON block: 31 | 32 | .. code:: json 33 | 34 | "metadata": { 35 | "kernelspec": { 36 | "display_name": "Python 3", 37 | "language": "python", 38 | "name": "python3" 39 | }, 40 | "labnote": { 41 | "title": "Notebook title" 42 | }, 43 | 44 | Recent versions of Jupyter notebook include an a built-in metadata editor, 45 | although any plain-text editor such as Vim will also work fine. 46 | 47 | Customizing categories 48 | ~~~~~~~~~~~~~~~~~~~~~~ 49 | 50 | Categories are used by Labnote to group related notebook entries. Each category 51 | may be assigned an image and description which may be used by Labnote themes to 52 | provide a custom appearance for each section of the notebook. See the 53 | :ref:`configuration` section on categories for more information. 54 | 55 | .. figure:: images/category_example.png 56 | :alt: Example of a notebook category which includes an image. 57 | 58 | Customizing individual entries 59 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 60 | 61 | In addition to the automatic processing of entries that labnote normally uses 62 | to render notebook entries, directory-specific `.labnote` files can also be 63 | used to control the behavior and appearance of entries. These are YAML files, 64 | and should follow the format: 65 | 66 | .. code:: yaml 67 | 68 | --- 69 | README.html: 70 | title: Custom Title 71 | pipeline.sh: 72 | title: My Interesting Analysis Pipeline 73 | 74 | Furthermore, `.labnote` files can be used to specify additional entry metadata 75 | that can't be automatically detected such as a description of the notebook 76 | entry and links to external resources such as web-pages, presentation slides, 77 | etc: 78 | 79 | .. code:: yaml 80 | 81 | --- 82 | README.html: 83 | title: Custom Title 84 | description: Description of the notebook entry 85 | links: 86 | - http://www.google.com 87 | - research/extra/presentation.ppt 88 | 89 | 90 | Themes 91 | ~~~~~~ 92 | 93 | Default theme 94 | ''''''''''''' 95 | 96 | Currently Labnote ships with a single theme, creatively named "default". This 97 | is the theme that is shown in the example screenshot. It uses the `Skeleton 98 | <http://getskeleton.com/>`__ CSS framework to produce a simple three column 99 | layout with notebook entries in the center and, optionally, category images on 100 | either side of the entries. 101 | 102 | Additional themes may be included in the future and users are welcome to 103 | contribute their own themes. See the development guide for more information on 104 | creating new themes. 105 | 106 | Customizing themes with CSS/JS 107 | '''''''''''''''''''''''''''''' 108 | 109 | Lab note provides two configuration option which allow you to specify custom 110 | CSS or JavaScript files which should be included, regardless of which theme 111 | you are using: 112 | 113 | * ``user_css`` 114 | * ``user_js`` 115 | 116 | These may be included in your config.yml, or specified at run-time, and will 117 | result in the specified files being included in the outputted HTML. 118 | 119 | For example, if you wanted to change the font used for the category headers, 120 | you could create a file named 'custom.css' containing: 121 | 122 | .. code:: css 123 | 124 | .category-header { 125 | font-family: 'helvetica', 'sans-serif'; 126 | } 127 | 128 | Edit your config so that `user_css` provides a path to `custom.css`, relative 129 | to where your notebook output is saved, and regenerate the notebook. Your 130 | category headers should now use the Helvetica font instead of the cursive font 131 | currently used. 132 | 133 | -------------------------------------------------------------------------------- /docs/images/category_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/khughitt/labnote/0c7815936f955f3609628f8a5cab28b6ed2451c4/docs/images/category_example.png -------------------------------------------------------------------------------- /docs/images/example_screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/khughitt/labnote/0c7815936f955f3609628f8a5cab28b6ed2451c4/docs/images/example_screenshot.png -------------------------------------------------------------------------------- /docs/images/example_screenshot_bioinf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/khughitt/labnote/0c7815936f955f3609628f8a5cab28b6ed2451c4/docs/images/example_screenshot_bioinf.png -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | :tocdepth: 3 2 | 3 | Welcome to Labnote's documentation! 4 | =================================== 5 | 6 | Overview 7 | ======== 8 | 9 | Labnote is a flexible and lightweight tool for generating 10 | HTML-based `electronic lab 11 | notebooks <https://en.wikipedia.org/wiki/Electronic_lab_notebook>`__. 12 | 13 | Rather than attempting to provide a unified tool for creating and sharing lab 14 | notebook entries, Labnote simply ties together existing documents and analyses 15 | outputs and builds and creates an HTML index of these resources. 16 | 17 | In short, it helps you go from something like this: 18 | 19 | :: 20 | 21 | ├── animal_behavior 22 | │   └── molothrus 23 | │   └── README.html 24 | ├── barnacles 25 | │   ├── cirripede-morphology 26 | │   │   └── README.html 27 | │   └── cirripede-taxonomy 28 | │   └── README.html 29 | └── finches 30 | ├── finch-beak-size-comparison 31 | │   └── beak_size.py 32 | ├── finch-foraging-strategies 33 | │   └── foraging-strategies.py 34 | └── natural-selection 35 | └── thoughts.txt 36 | 37 | To something like this: 38 | 39 | .. figure:: images/example_screenshot.png 40 | :alt: A simple lab notebook 41 | 42 | Labnote works by scanning a set of one or more directories for files 43 | matching a pattern that you specify as pertaining to notebook entries 44 | (e.g. a single log, script, or document describing some particular 45 | project or analysis.) It then constructs an HTML table of contents 46 | pointing to each of the matching files. By default, results are sorted 47 | by last-modified date. Categories can be defined and used to separate 48 | entries relating to different topics. 49 | 50 | In order to support as many different work styles as possible, labnote 51 | tries and make as few assumptions as possible about how your files are 52 | organized, and provides configuration options allowing for a wide range of 53 | directory structures and file types. 54 | 55 | Finally, labnote is designed to be extensible. While currently there is 56 | only a single no-frills theme, the 57 | `jinga2 <http://jinja.pocoo.org/docs/dev/>`__ templating system used by 58 | Labnote makes it trivial to create themes. 59 | 60 | Documentation 61 | ============= 62 | 63 | .. toctree:: 64 | :maxdepth: 2 65 | 66 | installation.rst 67 | configuration.rst 68 | features.rst 69 | development.rst 70 | 71 | -------------------------------------------------------------------------------- /docs/installation.rst: -------------------------------------------------------------------------------- 1 | Installation 2 | ============ 3 | 4 | Requirements 5 | ------------ 6 | 7 | To use labnote, you must have a recent version of 8 | `Python (>=3.3) <https://www.python.org/>`__) available on your machine. 9 | 10 | Additionally, labnote requires the following Python libraries: 11 | 12 | - `Beautiful Soup 4 <http://www.crummy.com/software/BeautifulSoup/>`__ 13 | - `Jinja2 <http://jinja.pocoo.org/docs/dev/>`__ 14 | - `PyYAML <http://pyyaml.org/>`__ 15 | 16 | If you are using pip to install labnote, all of the required 17 | dependencies should be automatically installed for you. 18 | 19 | Labnote is currently aimed at supporting Windows, Linux, and OS X setups. 20 | 21 | Installing labnote 22 | ------------------ 23 | 24 | To install the latest stable version of Labnote using 25 | `pip <https://docs.python.org/3.5/installing/index.html>`__, run: 26 | 27 | :: 28 | 29 | pip install labnote 30 | 31 | To install the most recent version of Labnote from Github, run: 32 | 33 | :: 34 | 35 | pip install git+https://github.com/khughitt/labnote 36 | 37 | To see a full list of options available for labnote, run ``labnote -h``: 38 | 39 | :: 40 | 41 | usage: labnote [-h] [-c CONFIG] [-i INPUT_DIRS [INPUT_DIRS ...]] 42 | [-o OUTPUT_FILE] [-u URL_PREFIX] [--print-config] 43 | [--user-css USER_CSS] [--user-js USER_JS] 44 | 45 | Generate HTML lab notebook. 46 | 47 | optional arguments: 48 | -h, --help show this help message and exit 49 | -c CONFIG, --config CONFIG 50 | Configuration filepath. (Will use configuration in 51 | $HOME/.config/labnote/config.yml, if it exists.) 52 | -i INPUT_DIRS [INPUT_DIRS ...], --input-dirs INPUT_DIRS [INPUT_DIRS ...] 53 | Input directory(s) containing notebook entries. 54 | -o OUTPUT_FILE, --output-file OUTPUT_FILE 55 | Location to output notebook HTML to. 56 | -u URL_PREFIX, --url-prefix URL_PREFIX 57 | Prefix to add to each entry URL. (Default: "") 58 | --print-config Prints the default configuration for Labnote to screen 59 | --user-css USER_CSS Custom stylesheet to use. 60 | --user-js USER_JS Custom javascript file to use. 61 | 62 | Testing installation 63 | -------------------- 64 | 65 | To generate the example notebook, ``cd`` to the labnote source directory and 66 | run: 67 | 68 | :: 69 | 70 | labnote -c example/example.config.yml \ 71 | -i example/research/*/* \ 72 | -o example/research/index.html 73 | 74 | If the installation is working, you should see output to the screen which looks 75 | like: 76 | 77 | :: 78 | 79 | - Using configuration: example/example.config.yml 80 | - Starting Labnote 81 | LOADING 82 | - Scanning for notebook entries in example/research/animal_behavior/molothrus 83 | - Scanning for notebook entries in example/research/barnacles/cirripede-morphology 84 | - Scanning for notebook entries in example/research/barnacles/cirripede-taxonomy 85 | - Scanning for notebook entries in example/research/finches/finch-beak-size-comparison 86 | - Scanning for notebook entries in example/research/finches/finch-foraging-strategies 87 | - Scanning for notebook entries in example/research/finches/natural-selection 88 | - Scanning for notebook entries in example/research/images/1854_Balanidae_F339.2_figlbp12.jpg 89 | - Scanning for notebook entries in example/research/images/a1417007h.jpg 90 | - Scanning for notebook entries in example/research/private/notes.html 91 | - Scanning for notebook entries in example/research/resources/css 92 | - Scanning for notebook entries in example/research/resources/img 93 | * Adding example/research/animal_behavior/molothrus/README.html 94 | * Adding example/research/barnacles/cirripede-morphology/README.html 95 | * Adding example/research/barnacles/cirripede-taxonomy/README.html 96 | * Adding example/research/finches/finch-beak-size-comparison/beak_size.py 97 | * Adding example/research/finches/finch-foraging-strategies/foraging-strategies.py 98 | * Adding example/research/finches/natural-selection/thoughts.txt 99 | - Finished 100 | - Generating notebook HTML 101 | - Saving notebook to example/research/index.html 102 | 103 | A file named ``index.html`` will be outputted ``example/`` 104 | directory and should look something like what is shown in the screenshot 105 | from the overview section of the documentation. 106 | 107 | Now you are ready to configure Labnote for your own files. 108 | 109 | Automating notebook generation 110 | ------------------------------ 111 | 112 | The easiest way to keep your lab notebook up-to-date is to set Labnote so that 113 | it is run every day. 114 | 115 | Labnote can be easily automated using 116 | `Cron <https://en.wikipedia.org/wiki/Cron>`__. For example, to have labnote 117 | regenerate your lab notebook once a day, run ``crontab -e`` to edit your 118 | user-level cron jobs, and add: 119 | 120 | :: 121 | 122 | @daily labnote 123 | 124 | If you have created a user configuration for labnote in 125 | ``$HOME/.config/labnote/config.yml``, then you are all set. Otherwise simply 126 | add whatever options you would use when calling Labnote from the command-line 127 | to the cronjob, e.g.: 128 | 129 | :: 130 | 131 | @daily labnote -c /path/to/config.yml 132 | 133 | For more information on how to create and customize cron jobs on your system, 134 | see the `Ubuntu Cron Tutorial <https://help.ubuntu.com/community/CronHowto>`__. 135 | 136 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | REM Command file for Sphinx documentation 4 | 5 | if "%SPHINXBUILD%" == "" ( 6 | set SPHINXBUILD=sphinx-build 7 | ) 8 | set BUILDDIR=_build 9 | set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . 10 | set I18NSPHINXOPTS=%SPHINXOPTS% . 11 | if NOT "%PAPER%" == "" ( 12 | set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% 13 | set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% 14 | ) 15 | 16 | if "%1" == "" goto help 17 | 18 | if "%1" == "help" ( 19 | :help 20 | echo.Please use `make ^<target^>` where ^<target^> is one of 21 | echo. html to make standalone HTML files 22 | echo. dirhtml to make HTML files named index.html in directories 23 | echo. singlehtml to make a single large HTML file 24 | echo. pickle to make pickle files 25 | echo. json to make JSON files 26 | echo. htmlhelp to make HTML files and a HTML help project 27 | echo. qthelp to make HTML files and a qthelp project 28 | echo. devhelp to make HTML files and a Devhelp project 29 | echo. epub to make an epub 30 | echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter 31 | echo. text to make text files 32 | echo. man to make manual pages 33 | echo. texinfo to make Texinfo files 34 | echo. gettext to make PO message catalogs 35 | echo. changes to make an overview over all changed/added/deprecated items 36 | echo. xml to make Docutils-native XML files 37 | echo. pseudoxml to make pseudoxml-XML files for display purposes 38 | echo. linkcheck to check all external links for integrity 39 | echo. doctest to run all doctests embedded in the documentation if enabled 40 | echo. coverage to run coverage check of the documentation if enabled 41 | goto end 42 | ) 43 | 44 | if "%1" == "clean" ( 45 | for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i 46 | del /q /s %BUILDDIR%\* 47 | goto end 48 | ) 49 | 50 | 51 | REM Check if sphinx-build is available and fallback to Python version if any 52 | %SPHINXBUILD% 1>NUL 2>NUL 53 | if errorlevel 9009 goto sphinx_python 54 | goto sphinx_ok 55 | 56 | :sphinx_python 57 | 58 | set SPHINXBUILD=python -m sphinx.__init__ 59 | %SPHINXBUILD% 2> nul 60 | if errorlevel 9009 ( 61 | echo. 62 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 63 | echo.installed, then set the SPHINXBUILD environment variable to point 64 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 65 | echo.may add the Sphinx directory to PATH. 66 | echo. 67 | echo.If you don't have Sphinx installed, grab it from 68 | echo.http://sphinx-doc.org/ 69 | exit /b 1 70 | ) 71 | 72 | :sphinx_ok 73 | 74 | 75 | if "%1" == "html" ( 76 | %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html 77 | if errorlevel 1 exit /b 1 78 | echo. 79 | echo.Build finished. The HTML pages are in %BUILDDIR%/html. 80 | goto end 81 | ) 82 | 83 | if "%1" == "dirhtml" ( 84 | %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml 85 | if errorlevel 1 exit /b 1 86 | echo. 87 | echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. 88 | goto end 89 | ) 90 | 91 | if "%1" == "singlehtml" ( 92 | %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml 93 | if errorlevel 1 exit /b 1 94 | echo. 95 | echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. 96 | goto end 97 | ) 98 | 99 | if "%1" == "pickle" ( 100 | %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle 101 | if errorlevel 1 exit /b 1 102 | echo. 103 | echo.Build finished; now you can process the pickle files. 104 | goto end 105 | ) 106 | 107 | if "%1" == "json" ( 108 | %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json 109 | if errorlevel 1 exit /b 1 110 | echo. 111 | echo.Build finished; now you can process the JSON files. 112 | goto end 113 | ) 114 | 115 | if "%1" == "htmlhelp" ( 116 | %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp 117 | if errorlevel 1 exit /b 1 118 | echo. 119 | echo.Build finished; now you can run HTML Help Workshop with the ^ 120 | .hhp project file in %BUILDDIR%/htmlhelp. 121 | goto end 122 | ) 123 | 124 | if "%1" == "qthelp" ( 125 | %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp 126 | if errorlevel 1 exit /b 1 127 | echo. 128 | echo.Build finished; now you can run "qcollectiongenerator" with the ^ 129 | .qhcp project file in %BUILDDIR%/qthelp, like this: 130 | echo.^> qcollectiongenerator %BUILDDIR%\qthelp\Labnote.qhcp 131 | echo.To view the help file: 132 | echo.^> assistant -collectionFile %BUILDDIR%\qthelp\Labnote.ghc 133 | goto end 134 | ) 135 | 136 | if "%1" == "devhelp" ( 137 | %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp 138 | if errorlevel 1 exit /b 1 139 | echo. 140 | echo.Build finished. 141 | goto end 142 | ) 143 | 144 | if "%1" == "epub" ( 145 | %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub 146 | if errorlevel 1 exit /b 1 147 | echo. 148 | echo.Build finished. The epub file is in %BUILDDIR%/epub. 149 | goto end 150 | ) 151 | 152 | if "%1" == "latex" ( 153 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 154 | if errorlevel 1 exit /b 1 155 | echo. 156 | echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. 157 | goto end 158 | ) 159 | 160 | if "%1" == "latexpdf" ( 161 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 162 | cd %BUILDDIR%/latex 163 | make all-pdf 164 | cd %~dp0 165 | echo. 166 | echo.Build finished; the PDF files are in %BUILDDIR%/latex. 167 | goto end 168 | ) 169 | 170 | if "%1" == "latexpdfja" ( 171 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 172 | cd %BUILDDIR%/latex 173 | make all-pdf-ja 174 | cd %~dp0 175 | echo. 176 | echo.Build finished; the PDF files are in %BUILDDIR%/latex. 177 | goto end 178 | ) 179 | 180 | if "%1" == "text" ( 181 | %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text 182 | if errorlevel 1 exit /b 1 183 | echo. 184 | echo.Build finished. The text files are in %BUILDDIR%/text. 185 | goto end 186 | ) 187 | 188 | if "%1" == "man" ( 189 | %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man 190 | if errorlevel 1 exit /b 1 191 | echo. 192 | echo.Build finished. The manual pages are in %BUILDDIR%/man. 193 | goto end 194 | ) 195 | 196 | if "%1" == "texinfo" ( 197 | %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo 198 | if errorlevel 1 exit /b 1 199 | echo. 200 | echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. 201 | goto end 202 | ) 203 | 204 | if "%1" == "gettext" ( 205 | %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale 206 | if errorlevel 1 exit /b 1 207 | echo. 208 | echo.Build finished. The message catalogs are in %BUILDDIR%/locale. 209 | goto end 210 | ) 211 | 212 | if "%1" == "changes" ( 213 | %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes 214 | if errorlevel 1 exit /b 1 215 | echo. 216 | echo.The overview file is in %BUILDDIR%/changes. 217 | goto end 218 | ) 219 | 220 | if "%1" == "linkcheck" ( 221 | %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck 222 | if errorlevel 1 exit /b 1 223 | echo. 224 | echo.Link check complete; look for any errors in the above output ^ 225 | or in %BUILDDIR%/linkcheck/output.txt. 226 | goto end 227 | ) 228 | 229 | if "%1" == "doctest" ( 230 | %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest 231 | if errorlevel 1 exit /b 1 232 | echo. 233 | echo.Testing of doctests in the sources finished, look at the ^ 234 | results in %BUILDDIR%/doctest/output.txt. 235 | goto end 236 | ) 237 | 238 | if "%1" == "coverage" ( 239 | %SPHINXBUILD% -b coverage %ALLSPHINXOPTS% %BUILDDIR%/coverage 240 | if errorlevel 1 exit /b 1 241 | echo. 242 | echo.Testing of coverage in the sources finished, look at the ^ 243 | results in %BUILDDIR%/coverage/python.txt. 244 | goto end 245 | ) 246 | 247 | if "%1" == "xml" ( 248 | %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml 249 | if errorlevel 1 exit /b 1 250 | echo. 251 | echo.Build finished. The XML files are in %BUILDDIR%/xml. 252 | goto end 253 | ) 254 | 255 | if "%1" == "pseudoxml" ( 256 | %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml 257 | if errorlevel 1 exit /b 1 258 | echo. 259 | echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. 260 | goto end 261 | ) 262 | 263 | :end 264 | -------------------------------------------------------------------------------- /examples/bioinformatics/example.config.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # General information 3 | title: Lab Notebook 4 | author: V. Keith Hughitt 5 | email: khughitt@umd.edu 6 | 7 | # Search path for notebook entries 8 | input_dirs: 9 | - research/* 10 | - research/*/* 11 | 12 | # Location where notebook HTML should be saved to 13 | output_file: research/index.html 14 | 15 | # File types to include 16 | include_files: ['*.html', '*.py', '*.ipynb'] 17 | 18 | # Files to exclude 19 | exclude: ['private'] 20 | 21 | # Research categories 22 | categories: 23 | Co-expression analysis: 24 | patterns: ['coex'] 25 | image: "images/coex-network.png" 26 | Host-pathogen network analysis: 27 | patterns: ['host-pat'] 28 | image: "images/hpi-network.png" 29 | Exploratory Data Analysis: 30 | patterns: ['exploratory'] 31 | image: "images/biplot.png" 32 | 33 | sort_categories_by_date: false 34 | 35 | # Notebook theme 36 | theme: default 37 | 38 | -------------------------------------------------------------------------------- /examples/bioinformatics/research/co-expression/hsapiens-coex-network/.labnote: -------------------------------------------------------------------------------- 1 | --- 2 | thoughts.txt: 3 | title: Natural Selection 4 | description: Some Thoughts on the Process of Natural Selection 5 | external: 6 | - https://en.wikipedia.org/wiki/Charles_Lyell 7 | - http://www.faculty.rsu.edu/users/f/felwell/www/Theorists/Essays/Malthus1.htm 8 | -------------------------------------------------------------------------------- /examples/bioinformatics/research/exploratory_data_analysis/biplot_eda.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": { 7 | "collapsed": true 8 | }, 9 | "outputs": [], 10 | "source": [] 11 | } 12 | ], 13 | "metadata": { 14 | "kernelspec": { 15 | "display_name": "Python 3", 16 | "language": "python", 17 | "name": "python3" 18 | }, 19 | "labnote": { 20 | "title": "Detecting latent features using biplots" 21 | }, 22 | "language_info": { 23 | "codemirror_mode": { 24 | "name": "ipython", 25 | "version": 3 26 | }, 27 | "file_extension": ".py", 28 | "mimetype": "text/x-python", 29 | "name": "python", 30 | "nbconvert_exporter": "python", 31 | "pygments_lexer": "ipython3", 32 | "version": "3.5.2" 33 | } 34 | }, 35 | "nbformat": 4, 36 | "nbformat_minor": 0 37 | } 38 | -------------------------------------------------------------------------------- /examples/bioinformatics/research/exploratory_data_analysis/nmf_comparison.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | """ 4 | Nonnegative matrix factorization comparison 5 | """ 6 | def main(): 7 | pass 8 | 9 | if __name__ == "__main__": 10 | main() 11 | -------------------------------------------------------------------------------- /examples/bioinformatics/research/host-pathogen_networks/hpi-module-network/hpi_network_construction.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | """ 4 | Host-pathogen module network construction 5 | """ 6 | def main(): 7 | pass 8 | 9 | if __name__ == "__main__": 10 | main() 11 | -------------------------------------------------------------------------------- /examples/bioinformatics/research/images/biplot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/khughitt/labnote/0c7815936f955f3609628f8a5cab28b6ed2451c4/examples/bioinformatics/research/images/biplot.png -------------------------------------------------------------------------------- /examples/bioinformatics/research/images/coex-network.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/khughitt/labnote/0c7815936f955f3609628f8a5cab28b6ed2451c4/examples/bioinformatics/research/images/coex-network.png -------------------------------------------------------------------------------- /examples/bioinformatics/research/images/hpi-network.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/khughitt/labnote/0c7815936f955f3609628f8a5cab28b6ed2451c4/examples/bioinformatics/research/images/hpi-network.png -------------------------------------------------------------------------------- /examples/darwin/example.config.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # General information 3 | title: Lab Notebook 4 | author: C. Darwin 5 | email: cdarwin@geolsoc.org.uk 6 | 7 | # Search path for notebook entries 8 | input_dirs: 9 | - example/research/*/* 10 | 11 | # Location where notebook HTML should be saved to 12 | output_file: example/research/index.html 13 | 14 | # File types to include 15 | include_files: ['*.html', '*.py', '*.txt'] 16 | 17 | # Files to exclude 18 | exclude: ['private'] 19 | 20 | # Research categories 21 | categories: 22 | Finches: 23 | patterns: ['finch', 'natural-selection'] 24 | description: "Finch research from the second voyage of HMS Beagle" 25 | image: "images/a1417007h.jpg" 26 | Barnacles: 27 | patterns: ['cirripede'] 28 | image: "images/1854_Balanidae_F339.2_figlbp12.jpg" 29 | 30 | # Short-hand method of specifying categories 31 | #categories: 32 | # 'Finches': ['finch', 'natural-selection'] 33 | # 'Barnacles': ['cirripede'] 34 | 35 | sort_categories_by_date: false 36 | 37 | # Notebook theme 38 | theme: default 39 | 40 | # 41 | # Image sources 42 | # ------------- 43 | # 44 | # 1. Tanagra Darwini, 1840-1843 45 | # State Library New South Wales 46 | # http://www2.sl.nsw.gov.au/archive/events/exhibitions/2007/heritage/images/9.html 47 | # 48 | ## 2. A monograph on the sub-class Cirripedia 49 | # Darwin Online 50 | # http://darwin-online.org.uk/content/frameset?itemID=F339.2&viewtype=side&pageseq=1 51 | # 52 | 53 | -------------------------------------------------------------------------------- /examples/darwin/research/animal_behavior/molothrus/README.html: -------------------------------------------------------------------------------- 1 | <!DOCTYPE html> 2 | <html lang="en"> 3 | <head> 4 | <meta charset="utf-8"> 5 | <title>Molothrus parasitism 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /examples/darwin/research/barnacles/cirripede-morphology/README.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Comparison of cirripede morphology 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /examples/darwin/research/barnacles/cirripede-taxonomy/README.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Cirripede systematics analysis 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /examples/darwin/research/finches/finch-beak-size-comparison/beak_size.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | """ 4 | Finch Beak Size Analysis 5 | """ 6 | def main(): 7 | pass 8 | 9 | if __name__ == "__main__": 10 | main() 11 | -------------------------------------------------------------------------------- /examples/darwin/research/finches/finch-foraging-strategies/foraging-strategies.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | def main(): 4 | pass 5 | 6 | if __name__ == "__main__": 7 | main() 8 | -------------------------------------------------------------------------------- /examples/darwin/research/finches/natural-selection/.labnote: -------------------------------------------------------------------------------- 1 | --- 2 | thoughts.txt: 3 | title: Natural Selection 4 | description: Some Thoughts on the Process of Natural Selection 5 | external: 6 | - https://en.wikipedia.org/wiki/Charles_Lyell 7 | - http://www.faculty.rsu.edu/users/f/felwell/www/Theorists/Essays/Malthus1.htm 8 | -------------------------------------------------------------------------------- /examples/darwin/research/finches/natural-selection/thoughts.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/khughitt/labnote/0c7815936f955f3609628f8a5cab28b6ed2451c4/examples/darwin/research/finches/natural-selection/thoughts.txt -------------------------------------------------------------------------------- /examples/darwin/research/images/1854_Balanidae_F339.2_figlbp12.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/khughitt/labnote/0c7815936f955f3609628f8a5cab28b6ed2451c4/examples/darwin/research/images/1854_Balanidae_F339.2_figlbp12.jpg -------------------------------------------------------------------------------- /examples/darwin/research/images/a1417007h.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/khughitt/labnote/0c7815936f955f3609628f8a5cab28b6ed2451c4/examples/darwin/research/images/a1417007h.jpg -------------------------------------------------------------------------------- /examples/darwin/research/private/notes.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Some private notes to be excluded... 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /labnote/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | labnote 3 | """ 4 | __version__ = 0.9 5 | 6 | from labnote.notebook import Notebook 7 | from labnote.entry import Entry 8 | from labnote.categories import CategoryManager 9 | -------------------------------------------------------------------------------- /labnote/categories.py: -------------------------------------------------------------------------------- 1 | """ 2 | CategoryManager class definition 3 | """ 4 | import fnmatch 5 | from collections import OrderedDict 6 | from datetime import datetime 7 | 8 | class CategoryManager(OrderedDict): 9 | def __init__(self, categories): 10 | # If passed a list of tuples, covert to OrderedList first 11 | # This occurs after the object has been sorted. 12 | if isinstance(categories, list): 13 | categories = OrderedDict(categories) 14 | 15 | # If no categories were specifed in config, add "Other" 16 | if len(categories) == 0: 17 | categories['Other'] = Category('Other', []) 18 | else: 19 | # If this is the first time CategoryManager is being created, we 20 | # need to initialize the Category instances from the relevant 21 | # settings; otherwise (e.g. after sorting), we can simply call the 22 | # parent constructor right away. 23 | key = list(categories.keys())[0] 24 | 25 | if not isinstance(categories[key], Category): 26 | 27 | # Extend category metadata with defaults 28 | for name,settings in categories.items(): 29 | # Dict of category metadata 30 | if isinstance(settings, dict): 31 | patterns = settings['patterns'] 32 | kwargs = settings 33 | del kwargs['patterns'] 34 | else: 35 | patterns = settings 36 | kwargs = {} 37 | 38 | # check to make sure patterns isn't a single string 39 | if isinstance(patterns, str): 40 | patterns = [patterns] 41 | 42 | categories[name] = Category(name, patterns, **kwargs) 43 | 44 | # Add blank "Other" category 45 | if 'Other' not in categories.keys(): 46 | categories['Other'] = Category('Other', []) 47 | 48 | super().__init__(categories) 49 | 50 | def _get_category(self, entry, default='Other'): 51 | """Determines category for a given analysis""" 52 | for name,category in self.items(): 53 | if any(fnmatch.fnmatch(entry.url, ("*%s*" % p)) for p in 54 | category.patterns): 55 | return(name) 56 | return(default) 57 | 58 | def add_entry(self, entry, category=None): 59 | """Adds a single entry to the CategoryManager""" 60 | if category is None: 61 | # if no category is specified, check to see if entry matches 62 | # any of the known category search patterns 63 | category = self._get_category(entry) 64 | elif category not in self: 65 | # for external entries, category may not yet exist 66 | self[category] = Category(category, []) 67 | 68 | self[category].append(entry) 69 | 70 | def get_entries(self): 71 | """Returns a flattened list of all entries""" 72 | entries = [] 73 | for category in self.values(): 74 | entries = entries + category 75 | return(entries) 76 | 77 | def sort_entries(self, by_date): 78 | """Sorts entries within each category""" 79 | # Sort by title or date 80 | for category in self: 81 | if by_date: 82 | self[category].sort(reverse=True) 83 | else: 84 | self[category].sort(key=lambda x: str(getattr(x, 'title')).lower()) 85 | 86 | #for category in self: 87 | # self[category] = Category(category, sorted( 88 | # self[category], 89 | # key=lambda x: str(getattr(x, sort_key)).lower(), 90 | # reverse=by_date 91 | # )) 92 | 93 | class Category(list): 94 | """Notebook Category class definition""" 95 | def __init__(self, name, patterns, description='', image=''): 96 | """Creates a new Category instance""" 97 | self.name = name 98 | self.patterns = patterns 99 | self.description = description 100 | self.image = image 101 | self.last_modified = datetime.fromtimestamp(0) 102 | super().__init__() 103 | 104 | def __repr__(self): 105 | return "Category([%s])" % (", ".join(x.title for x in self)) 106 | 107 | def __lt__(self, other): 108 | """Allow sorting of categories by last modified date""" 109 | return self.last_modified < other.self_modified 110 | 111 | def append(self, entry): 112 | super().append(entry) 113 | 114 | if self.last_modified < entry.date: 115 | self.last_modified = entry.date 116 | 117 | 118 | -------------------------------------------------------------------------------- /labnote/entry.py: -------------------------------------------------------------------------------- 1 | """ 2 | Labnote Entry class definitions. 3 | """ 4 | import os 5 | from datetime import datetime 6 | from urllib.parse import urljoin 7 | from urllib.request import pathname2url 8 | 9 | class Entry(object): 10 | """Base notebook Entry class""" 11 | def __init__(self, **kwargs): 12 | """Creates a lab notebook Entry object instance. 13 | 14 | Parses the relevant file corresponding to the notebook entry and 15 | attempts to determine an appropriate title to use for the entry, the 16 | date the analysis was last modified, etc. and stores all of the 17 | relevant information as a Entry instance. 18 | 19 | Args: 20 | filepath: Path to the file for which the lab notebook entry is 21 | being created. 22 | output_dir: The notebook HTML output directory. This will be 23 | removed from the final path in order to generate a relative URL. 24 | url_prefix: An optional URL prefix to be preprended to the entry. 25 | """ 26 | self.filepath = kwargs['filepath'] 27 | self.filename = os.path.basename(self.filepath) 28 | self.dir_name = os.path.basename(os.path.dirname(self.filepath)) 29 | self.date = datetime.fromtimestamp(os.path.getmtime(self.filepath)) 30 | self.url = pathname2url(urljoin(kwargs['url_prefix'], 31 | self.filepath.replace(kwargs['output_dir'] + "/", ''))) 32 | 33 | # set title 34 | if 'title' in kwargs: 35 | self.title = kwargs['title'] 36 | else: 37 | self.title = self._get_entry_title() 38 | 39 | def __lt__(self, other): 40 | """Allow entries to be sorted by date""" 41 | return self.date < other.date 42 | 43 | @staticmethod 44 | def factory(**kwargs): 45 | """Static method used to create specific entry instances""" 46 | # Local entry 47 | if 'filepath' in kwargs: 48 | print(" * Adding %s" % kwargs['filepath']) 49 | 50 | # Determine file extension 51 | ext = os.path.splitext(kwargs['filepath'])[-1].lower() 52 | 53 | # HTML files 54 | if ext == '.html': 55 | return HTMLEntry(**kwargs) 56 | elif ext == '.py': 57 | # Python scripts 58 | return PythonEntry(**kwargs) 59 | elif ext == '.ipynb': 60 | # IPython notebook 61 | return JupyterEntry(**kwargs) 62 | else: 63 | # Everything else 64 | return GenericEntry(**kwargs) 65 | elif 'url' in kwargs: 66 | # External entry 67 | return ExternalEntry(**kwargs) 68 | 69 | class HTMLEntry(Entry): 70 | """HTML lab notebook entry""" 71 | def __init__(self, **kwargs): 72 | """Creates a new HTMLEntry instance.""" 73 | super().__init__(**kwargs) 74 | 75 | def _get_entry_title(self): 76 | """Determine title to use for the specified notebook entry""" 77 | from bs4 import BeautifulSoup 78 | 79 | with open(self.filepath) as fp: 80 | soup = BeautifulSoup(fp, 'html.parser') 81 | 82 | # If no title is found, use filename instead 83 | # TODO: print warning? 84 | if soup.title is None or soup.title.string is None: 85 | return self.filename 86 | else: 87 | return soup.title.string 88 | 89 | class GenericEntry(Entry): 90 | """Generic lab notebook entry""" 91 | def __init__(self, **kwargs): 92 | """Creates a new GenericEntry instance.""" 93 | super().__init__(**kwargs) 94 | 95 | def _get_entry_title(self): 96 | """Determine title to use for the specified notebook entry""" 97 | return self.filename 98 | 99 | class PythonEntry(Entry): 100 | """Python lab notebook entry""" 101 | def __init__(self, **kwargs): 102 | """Creates a new PythonEntry instance.""" 103 | super().__init__(**kwargs) 104 | 105 | def _get_entry_title(self): 106 | """Attempts to extract the first line of a python file docstring to use 107 | as a notebook entry title""" 108 | import ast 109 | 110 | with open(self.filepath) as fp: 111 | # load python code 112 | try: 113 | mod = ast.parse(''.join(fp)) 114 | except SyntaxError: 115 | # Non-standard Python code (e.g. Snakemake) 116 | return self.filename 117 | 118 | # grab docstring if it exists 119 | docstring = ast.get_docstring(mod) 120 | 121 | if docstring is not None: 122 | # extract first line from docstring 123 | return docstring.split('\n')[0] 124 | else: 125 | return self.filename 126 | 127 | class JupyterEntry(Entry): 128 | """Jupyter/IPython notebook entry""" 129 | def __init__(self, **kwargs): 130 | """Creates a new JupyterEntry instance.""" 131 | super().__init__(**kwargs) 132 | 133 | def _get_entry_title(self): 134 | """Determine title to use for the specified notebook entry""" 135 | import json 136 | 137 | with open(self.filepath) as fp: 138 | contents = json.load(fp) 139 | metadata = contents['metadata'] 140 | 141 | # If no title is found, use filename 142 | if 'labnote' in metadata and 'title' in metadata['labnote']: 143 | return metadata['labnote']['title'] 144 | else: 145 | return self.filename.replace('.ipynb', '') 146 | 147 | class ExternalEntry(object): 148 | """External lab notebook entry 149 | 150 | NOTE: This class currently does not sub-class from the base Entry class. 151 | Eventually it may be helpful to create create a middle layer with 152 | "LocalEntry" and "ExternalEntry" or "RemoteEntry", from which each of 153 | the subclasses can inherit from. 154 | 155 | Another possibility would be to make the local or remote features 156 | decorators of each of the Entry subclasses. 157 | """ 158 | def __init__(self, **kwargs): 159 | """Creates a new ExternalEntry instance.""" 160 | from urllib import request 161 | from urllib.parse import urlsplit 162 | from urllib.request import HTTPError,URLError 163 | 164 | # Required variables - title & url 165 | self.title = kwargs['title'] 166 | self.url = kwargs['url'] 167 | 168 | # Parse filename and directory from URL 169 | PATH_IDX = 2 170 | 171 | url_parts = urlsplit(self.url) 172 | url_path = url_parts[PATH_IDX] 173 | 174 | self.filename = os.path.basename(url_path) 175 | self.dir_name = os.path.basename(os.path.dirname(url_path)) 176 | 177 | # Attempt to determine the date last modified 178 | try: 179 | conn = request.urlopen(self.url) 180 | 181 | if 'last-modified' in conn.headers: 182 | import email.utils as eut 183 | self.date = datetime(*eut.parsedate(conn.headers['last-modified'])[:6]) 184 | else: 185 | # If date not specified, default to unix timestamp 0 186 | self.date = datetime.fromtimestamp(0) 187 | except (HTTPError, URLError, ConnectionRefusedError): 188 | # If URL is not accessible, default to unix timestamp 0 189 | self.date = datetime.fromtimestamp(0) 190 | 191 | def _get_entry_title(self): 192 | """Determine title to use for the specified notebook entry""" 193 | return self.title 194 | 195 | def __lt__(self, other): 196 | """Allow entries to be sorted by date""" 197 | return self.date < other.date 198 | 199 | -------------------------------------------------------------------------------- /labnote/notebook.py: -------------------------------------------------------------------------------- 1 | """ 2 | Notebook class definition 3 | """ 4 | import fnmatch 5 | import glob 6 | import os 7 | import platform 8 | import sys 9 | import time 10 | import yaml 11 | from argparse import ArgumentParser 12 | from collections import OrderedDict 13 | from labnote.categories import CategoryManager 14 | from labnote.entry import Entry 15 | from labnote.renderer import HTMLRenderer 16 | 17 | class Notebook(object): 18 | """Notebook class""" 19 | def __init__(self, conf=None, **kwargs): 20 | """Creates a new notebook instance""" 21 | # Get default args 22 | config = self._load_config(conf, **kwargs) 23 | 24 | print("- Starting Labnote") 25 | print(" LOADING") 26 | 27 | # Set object attributes 28 | self.author = config['author'] 29 | self.email = config['email'] 30 | self.exclude = config['exclude'] 31 | self.external = config['external'] 32 | self.title = config['title'] 33 | self.input_dirs = config['input_dirs'] 34 | self.output_file = config['output_file'] 35 | self.include_files = config['include_files'] 36 | self.theme = config['theme'] 37 | self.user_css = config['user_css'] 38 | self.user_js = config['user_js'] 39 | self.url_prefix = config['url_prefix'] 40 | self.sort_categories_by_date = config['sort_categories_by_date'] 41 | self.sort_entries_by_date = config['sort_entries_by_date'] 42 | 43 | # Get current date string 44 | self.date = time.strftime('%Y/%m/%d') 45 | 46 | # Load categories 47 | self.entries = CategoryManager(config['categories']) 48 | 49 | # Find valid notebook entry directories 50 | self._parse_entries() 51 | 52 | self._sort_notebook_entries() 53 | 54 | # Create a Renderer instance 55 | self.renderer = HTMLRenderer(self.author, self.title, self.email, 56 | self.date, self.entries, self.output_file, 57 | self.user_css, self.user_js, 58 | self.theme) 59 | print("- Finished") 60 | 61 | def _find_valid_files(self): 62 | """Search specified locations for files that corresponding to lab 63 | notebook entries. 64 | 65 | The lab notebook consists of a collection of entries, each 66 | corresponding to a particular analysis, script, etc. Labnote searches 67 | the specified input paths for any files matching the allowed file types 68 | (e.g. *.html, *.py), and adds an entry for each item in the resulting 69 | notebook. This is the function which scans for acceptable files to 70 | build entries from, and produces a list of filepaths which can then be 71 | converted to entry dicts. 72 | 73 | Returns 74 | ------- 75 | A list of filepaths corresponding to items that should form the 76 | basis of the lab notebook entries. 77 | """ 78 | filepaths = [] 79 | 80 | # Iterate over sub-directories in each search path 81 | for input_dir in self.input_dirs: 82 | print("- Scanning for notebook entries in %s" % input_dir) 83 | 84 | for sub_dir in glob.glob(input_dir): 85 | if not os.path.isdir(sub_dir): 86 | continue 87 | 88 | for filename in os.listdir(sub_dir): 89 | filepath = os.path.join(sub_dir, filename) 90 | if not os.path.isfile(filepath): 91 | continue 92 | 93 | # Skip files which match one of the exclude patterns 94 | if any([x in filepath for x in self.exclude]): 95 | continue 96 | 97 | # For each file in the top-level of a matching dir, check 98 | # to see if it is a valid notebook entry file 99 | if any(fnmatch.fnmatch(filename, pattern) for 100 | pattern in self.include_files): 101 | filepaths.append(filepath) 102 | 103 | return filepaths 104 | 105 | def _parse_entries(self): 106 | """Creates notebook entries""" 107 | 108 | # Find files to use for notebook generation 109 | filepaths = self._find_valid_files() 110 | 111 | # Directory where results will be outputted 112 | output_dir = os.path.dirname(self.output_file) 113 | 114 | # Iterate over matches files and create notebook entries 115 | for filepath in filepaths: 116 | # Check for .labnote file in directory 117 | metafile = os.path.join(os.path.dirname(filepath), '.labnote') 118 | 119 | kwargs = {} 120 | 121 | if os.path.exists(metafile): 122 | with open(metafile) as fp: 123 | metadata = yaml.load(fp) 124 | 125 | filename = os.path.basename(filepath) 126 | if filename in metadata.keys(): 127 | kwargs = metadata[filename] 128 | 129 | # Add filepath, output_dir, and url_prefix to kwargs 130 | kwargs['filepath'] = filepath 131 | kwargs['output_dir'] = output_dir 132 | kwargs['url_prefix'] = self.url_prefix 133 | 134 | # Create a new notebook Entry instance 135 | entry = Entry.factory(**kwargs) 136 | 137 | # Add entry 138 | if 'category' in kwargs: 139 | self.entries.add_entry(entry, kwargs['category']) 140 | else: 141 | self.entries.add_entry(entry) 142 | 143 | # Add any external entries 144 | for name in self.external: 145 | kwargs = self.external[name] 146 | kwargs['title'] = name 147 | 148 | entry = Entry.factory(**kwargs) 149 | 150 | # Add entry 151 | if 'category' in kwargs: 152 | self.entries.add_entry(entry, kwargs['category']) 153 | else: 154 | self.entries.add_entry(entry) 155 | 156 | # Remove any categories for which no entries were found 157 | for category in list(self.entries.keys()): 158 | if len(self.entries[category]) == 0: 159 | del self.entries[category] 160 | 161 | def _sort_notebook_entries(self): 162 | """Sorts notebook entries""" 163 | # Sort entries within each category 164 | self.entries.sort_entries(by_date=self.sort_entries_by_date) 165 | 166 | # Sort categories by order of date last modified 167 | if self.sort_categories_by_date: 168 | self.entries = CategoryManager( 169 | sorted(self.entries.items(), 170 | key=lambda x: x[1].last_modified, 171 | reverse=True) 172 | ) 173 | 174 | def _load_config(self, config_filepath, **kwargs): 175 | """Loads labnote configuration 176 | 177 | This function determines which settings to use when running Labnote. 178 | Settings may be specified in several different ways. The order to 179 | precedence is: 180 | 181 | 1. Kwargs specified in Notebook constructor 182 | 2. Configuration file specified by `conf` argument to `_load_config` 183 | 3. Command-line options 184 | 4. User configuration (~/.config/labnote/config.yaml) 185 | 5. Defaults 186 | 187 | Args: 188 | config_filepath: (Optional) Configuration filepath to use. 189 | kwargs: (Optional) Arguments specified via Notebook constructor. 190 | 191 | Returns: 192 | config: OrderedDict containing labnote settings. 193 | """ 194 | # Load configuration 195 | parser = self._get_args() 196 | 197 | # Convert input arguments to a python dict 198 | args = parser.parse_args() 199 | args = dict((k, v) for k, v in list(vars(args).items()) if v is not None) 200 | 201 | # Default configuration options 202 | config = self._get_defaults() 203 | 204 | # If requested, print default configuration and exit 205 | if args['print_config']: 206 | self.print_config() 207 | sys.exit() 208 | 209 | # If user specified a configuration filepath in the command, use that path 210 | if 'config' in args: 211 | if not os.path.isfile(args['config']): 212 | print("Invalid configuration path specified: %s" % args['config']) 213 | sys.exit() 214 | 215 | print("- Using configuration: %s" % args['config']) 216 | 217 | # Load config specified from run arguments 218 | with open(args['config']) as fp: 219 | config.update(self._ordered_load(fp)) 220 | else: 221 | # Check for configuration file specified in the Notebook 222 | # constructor. 223 | if config_filepath is not None: 224 | config_file = config_filepath 225 | if not os.path.isfile(config_file): 226 | print("Invalid configuration path specified: %s" % args['config']) 227 | sys.exit() 228 | else: 229 | # Otherwise, load user config file if it exists 230 | # Windows 231 | if platform.system() == 'Windows': 232 | config_dir = os.path.join(os.environ['APPDATA'], 'labnote') 233 | else: 234 | # Linux / OS X 235 | config_dir = os.path.expanduser("~/.config/labnote/") 236 | 237 | # Check for config.yaml or config.yml 238 | config_file = os.path.join(config_dir, 'config.yml') 239 | if not os.path.isfile(config_file): 240 | config_file = os.path.join(config_dir, 'config.yaml') 241 | 242 | # If user config exists, use it to overwrite defaults 243 | if os.path.isfile(config_file): 244 | print("- Using configuration: %s" % config_file) 245 | with open(config_file) as fp: 246 | config.update(self._ordered_load(fp)) 247 | 248 | # Update default arguments with user-specified settings 249 | config.update(args) 250 | 251 | # Update with arguments specified to Notebook constructor 252 | config.update(kwargs) 253 | 254 | # For arguments which accept lists or strings, convert single string 255 | # values into lists 256 | for key in ['input_dirs', 'exclude', 'include_files']: 257 | if isinstance(config[key], str): 258 | config[key] = [config[key]] 259 | 260 | # Validate configuration 261 | self._check_config(config, parser) 262 | 263 | return config 264 | 265 | def _check_config(self, config, parser): 266 | """Checks configuration to make sure it is valid""" 267 | # Required arguments 268 | if 'input_dirs' not in config: 269 | parser.print_help() 270 | print("Error: missing input directory(s).") 271 | sys.exit() 272 | elif 'output_file' not in config: 273 | parser.print_help() 274 | print("Error: missing output filepath.") 275 | sys.exit() 276 | 277 | # Check for proper types 278 | expected_types = { 279 | 'title': str, 280 | 'author': str, 281 | 'email': str, 282 | 'exclude': list, 283 | 'external': dict, 284 | 'include_files': list, 285 | 'input_dirs': list, 286 | 'output_file': str, 287 | 'sort_categories_by_date': bool, 288 | 'sort_entries_by_date': bool, 289 | 'theme': str, 290 | 'url_prefix': str, 291 | 'user_css': str, 292 | 'user_js': str 293 | } 294 | 295 | for key in expected_types: 296 | if not isinstance(config[key], expected_types[key]): 297 | parser.print_help() 298 | print("Invalid argument specified for %s" % key) 299 | sys.exit() 300 | 301 | # Check to make sure a valid theme was specified 302 | # TODO: modify to check directory of themes 303 | if config['theme'] not in ['default']: 304 | parser.print_help() 305 | print("Invalid theme specified.") 306 | sys.exit() 307 | 308 | # Check to make sure output directory exists 309 | output_dir = os.path.dirname(config['output_file']) 310 | 311 | if not os.path.isdir(output_dir): 312 | parser.print_help() 313 | print("Output directory (%s) does not exist!" % output_dir) 314 | sys.exit() 315 | 316 | def _get_args(self): 317 | """Parses input and returns arguments""" 318 | parser = ArgumentParser(description='Generate HTML lab notebook.') 319 | 320 | parser.add_argument('-c', '--config', 321 | help=('Configuration filepath. (Will use configuration ' 322 | 'in $HOME/.config/labnote/config.yml, if it exists.)')) 323 | parser.add_argument('-i', '--input-dirs', dest='input_dirs', nargs='+', 324 | help='Input directory(s) containing notebook entries.') 325 | parser.add_argument('-o', '--output-file', dest='output_file', 326 | help=('Location to output notebook HTML to.')) 327 | parser.add_argument('-u', '--url-prefix', dest='url_prefix', 328 | help='Prefix to add to each entry URL. (Default: "")') 329 | parser.add_argument('--print-config', dest='print_config', 330 | action='store_true', 331 | help=('Prints the default configuration for ' 332 | 'Labnote to screen')) 333 | parser.add_argument('--user-css', dest='user_css', 334 | help='Custom stylesheet to use.') 335 | parser.add_argument('--user-js', dest='user_js', 336 | help='Custom javascript file to use.') 337 | 338 | return parser 339 | 340 | # Default options 341 | def _get_defaults(self): 342 | """Gets a dictionary of default options""" 343 | return { 344 | 'title': 'Lab Notebook', 345 | 'author': '', 346 | 'categories': [], 347 | 'email': '', 348 | 'entries': {}, 349 | 'exclude': [], 350 | 'external': {}, 351 | 'include_files': ['*.html', '*.py', '*.ipynb'], 352 | 'input_dirs': None, 353 | 'output_file': None, 354 | 'sort_categories_by_date': True, 355 | 'sort_entries_by_date': False, 356 | 'theme': 'default', 357 | 'url_prefix': '', 358 | 'user_css': '', 359 | 'user_js': '' 360 | } 361 | 362 | def print_config(self): 363 | """Prints an example config file which can be edited and used 364 | as a starting point.""" 365 | config = { 366 | 'title': 'Lab Notebook', 367 | 'author': '', 368 | 'email': '', 369 | 'input_dirs': ['/var/www/research/one/*', 370 | '/var/www/research/two/*'], 371 | 'output_file': '/var/www/research/index.html', 372 | 'include_files': ['*.html', '*.py'] 373 | } 374 | print(yaml.dump(config, default_flow_style=False)) 375 | 376 | def _ordered_load(self, stream, Loader=yaml.Loader, 377 | object_pairs_hook=OrderedDict): 378 | """ 379 | Order-preserving YAML parser 380 | 381 | Creates a python representation of a YAML file in the usual manner, 382 | except that dictionaries are stored as OrderedDict instances, 383 | preserving the order in which they appear in the YAML file. 384 | 385 | Source: http://stackoverflow.com/a/21912744/554531 386 | 387 | Args: 388 | stream: The YAML source stream 389 | Loader: Base YAML loader class (default: yaml.Loader) 390 | object_pairs_hook: Class to use for storing dicts. (default: 391 | OrderedDict) 392 | Returns: 393 | An OrderedDict or similar instance representation of the input YAML. 394 | """ 395 | 396 | class OrderedLoader(Loader): 397 | pass 398 | def construct_mapping(loader, node): 399 | loader.flatten_mapping(node) 400 | return object_pairs_hook(loader.construct_pairs(node)) 401 | OrderedLoader.add_constructor( 402 | yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG, 403 | construct_mapping) 404 | return yaml.load(stream, OrderedLoader) 405 | 406 | def render(self): 407 | """Renders the notebook into HTML""" 408 | self.renderer.render() 409 | 410 | -------------------------------------------------------------------------------- /labnote/renderer.py: -------------------------------------------------------------------------------- 1 | """ 2 | Notebook rendering classes 3 | 4 | Currently, only an HTMLRenderer class is available. Eventually this could be 5 | extended to include something like a MarkdownRenderer class for hosting on 6 | Github, etc. 7 | """ 8 | import os 9 | import shutil 10 | from jinja2 import Environment, PackageLoader 11 | from pkg_resources import resource_filename, Requirement 12 | 13 | class Renderer(object): 14 | """Base notebook Renderer class""" 15 | def __init__(self, author, title, email, date, entries, 16 | output_file, user_css, user_js, theme='default'): 17 | self.author = author 18 | self.title = title 19 | self.email = email 20 | self.date = date 21 | self.entries = entries 22 | self.output_file = output_file 23 | self.user_css = user_css 24 | self.user_js = user_js 25 | self.theme = '%s.html' % theme 26 | 27 | # Load Jinja2 template 28 | env = Environment(loader=PackageLoader('labnote', 'templates')) 29 | self.template = env.get_template(self.theme) 30 | 31 | def render(self): 32 | """Abstract method for rendering the notebook""" 33 | pass 34 | 35 | class HTMLRenderer(Renderer): 36 | """HTML notebook renderer""" 37 | def __init__(self, author, title, email, date, entries, output_file, 38 | user_css, user_js, template): 39 | super().__init__(author, title, email, date, entries, output_file, 40 | user_css, user_js, template) 41 | 42 | def render(self): 43 | """Renders notebook""" 44 | html = self.template.render(author=self.author, title=self.title, 45 | email=self.email, date=self.date, 46 | user_css=self.user_css, 47 | user_js=self.user_js, 48 | entries=self.entries) 49 | 50 | print("- Generating notebook HTML") 51 | 52 | # Output notebook 53 | with open(self.output_file, 'w') as fp: 54 | fp.write(html) 55 | 56 | print("- Saving notebook to %s" % self.output_file) 57 | 58 | # Path to resources/ directory 59 | resources = resource_filename(Requirement.parse('labnote'), 60 | os.path.join('labnote', 'resources')) 61 | 62 | img_resources = os.path.join(resources, 'img') 63 | css_resources = os.path.join(resources, 'css') 64 | 65 | # Copy CSS and image resources to output directory 66 | output_base = os.path.join(os.path.dirname(self.output_file), 67 | 'resources') 68 | output_img = os.path.join(output_base, 'img') 69 | output_css = os.path.join(output_base, 'css') 70 | 71 | # Remove existing img/ and css/ directories 72 | for x in [output_img, output_css]: 73 | if os.path.isdir(x): 74 | shutil.rmtree(x) 75 | 76 | ignore_pattern = shutil.ignore_patterns("__init__.py", "__pycache__") 77 | 78 | shutil.copytree(img_resources, output_img, ignore=ignore_pattern) 79 | shutil.copytree(css_resources, output_css, ignore=ignore_pattern) 80 | 81 | -------------------------------------------------------------------------------- /labnote/resources/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/khughitt/labnote/0c7815936f955f3609628f8a5cab28b6ed2451c4/labnote/resources/__init__.py -------------------------------------------------------------------------------- /labnote/resources/css/default.css: -------------------------------------------------------------------------------- 1 | /** Lab notebook styles */ 2 | /** 3 | * Gradient horizontal ruler 4 | * http://jsfiddle.net/chriscoyier/GaEzp/35/ 5 | */ 6 | hr { 7 | border: 0; 8 | height: 1px; 9 | background-image: -webkit-linear-gradient(left, rgba(0,0,0,0), rgba(0,0,0,0.75), rgba(0,0,0,0)); 10 | background-image: -moz-linear-gradient(left, rgba(0,0,0,0), rgba(0,0,0,0.75), rgba(0,0,0,0)); 11 | background-image: -ms-linear-gradient(left, rgba(0,0,0,0), rgba(0,0,0,0.75), rgba(0,0,0,0)); 12 | background-image: -o-linear-gradient(left, rgba(0,0,0,0), rgba(0,0,0,0.75), rgba(0,0,0,0)); 13 | opacity: 0.75; 14 | } 15 | 16 | /** Header */ 17 | #header { 18 | text-align: center; 19 | margin-top: 1.5rem; 20 | } 21 | #header h1 { 22 | font-family: monospace, sans-serif; 23 | font-size: 5em; 24 | font-weight: 500; 25 | } 26 | 27 | #header h1, h3 { 28 | margin: 0; 29 | line-height: 1.1em; 30 | } 31 | #last-update { 32 | font-size: 1.2em; 33 | } 34 | 35 | /** Category */ 36 | .category-header { 37 | font-family: 'Pacifico', cursive; 38 | font-size: 2.3em; 39 | color: #3C3C3C; 40 | text-shadow: #cccccc 5px 5px 15px; 41 | } 42 | .category-image { 43 | max-width: 100%; 44 | } 45 | 46 | /** Entry **/ 47 | ul { 48 | font-family: 'Ubuntu', sans-serif; 49 | font-size: 1.3em; 50 | list-style: none; 51 | } 52 | li { 53 | white-space: nowrap; 54 | overflow: hidden; 55 | text-overflow: ellipsis; 56 | margin-bottom: auto; 57 | } 58 | .category { 59 | line-height: 2.75rem; 60 | color: #404040; 61 | } 62 | .category time { 63 | font-family: 'courier new', monospace; 64 | margin-right: 1.5rem; 65 | } 66 | 67 | /** External link icons **/ 68 | img.slideshow { 69 | content: url(../img/libreoffice-impress.png); 70 | width: 16px; 71 | height: 16px; 72 | } 73 | 74 | /** Link styles */ 75 | a, a:visited { color: #404040; text-decoration: none; } 76 | a:hover, a:visited:hover { color: #ff5900; } 77 | 78 | -------------------------------------------------------------------------------- /labnote/resources/css/normalize.css: -------------------------------------------------------------------------------- 1 | /*! normalize.css v3.0.2 | MIT License | git.io/normalize */ 2 | 3 | /** 4 | * 1. Set default font family to sans-serif. 5 | * 2. Prevent iOS text size adjust after orientation change, without disabling 6 | * user zoom. 7 | */ 8 | 9 | html { 10 | font-family: sans-serif; /* 1 */ 11 | -ms-text-size-adjust: 100%; /* 2 */ 12 | -webkit-text-size-adjust: 100%; /* 2 */ 13 | } 14 | 15 | /** 16 | * Remove default margin. 17 | */ 18 | 19 | body { 20 | margin: 0; 21 | } 22 | 23 | /* HTML5 display definitions 24 | ========================================================================== */ 25 | 26 | /** 27 | * Correct `block` display not defined for any HTML5 element in IE 8/9. 28 | * Correct `block` display not defined for `details` or `summary` in IE 10/11 29 | * and Firefox. 30 | * Correct `block` display not defined for `main` in IE 11. 31 | */ 32 | 33 | article, 34 | aside, 35 | details, 36 | figcaption, 37 | figure, 38 | footer, 39 | header, 40 | hgroup, 41 | main, 42 | menu, 43 | nav, 44 | section, 45 | summary { 46 | display: block; 47 | } 48 | 49 | /** 50 | * 1. Correct `inline-block` display not defined in IE 8/9. 51 | * 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera. 52 | */ 53 | 54 | audio, 55 | canvas, 56 | progress, 57 | video { 58 | display: inline-block; /* 1 */ 59 | vertical-align: baseline; /* 2 */ 60 | } 61 | 62 | /** 63 | * Prevent modern browsers from displaying `audio` without controls. 64 | * Remove excess height in iOS 5 devices. 65 | */ 66 | 67 | audio:not([controls]) { 68 | display: none; 69 | height: 0; 70 | } 71 | 72 | /** 73 | * Address `[hidden]` styling not present in IE 8/9/10. 74 | * Hide the `template` element in IE 8/9/11, Safari, and Firefox < 22. 75 | */ 76 | 77 | [hidden], 78 | template { 79 | display: none; 80 | } 81 | 82 | /* Links 83 | ========================================================================== */ 84 | 85 | /** 86 | * Remove the gray background color from active links in IE 10. 87 | */ 88 | 89 | a { 90 | background-color: transparent; 91 | } 92 | 93 | /** 94 | * Improve readability when focused and also mouse hovered in all browsers. 95 | */ 96 | 97 | a:active, 98 | a:hover { 99 | outline: 0; 100 | } 101 | 102 | /* Text-level semantics 103 | ========================================================================== */ 104 | 105 | /** 106 | * Address styling not present in IE 8/9/10/11, Safari, and Chrome. 107 | */ 108 | 109 | abbr[title] { 110 | border-bottom: 1px dotted; 111 | } 112 | 113 | /** 114 | * Address style set to `bolder` in Firefox 4+, Safari, and Chrome. 115 | */ 116 | 117 | b, 118 | strong { 119 | font-weight: bold; 120 | } 121 | 122 | /** 123 | * Address styling not present in Safari and Chrome. 124 | */ 125 | 126 | dfn { 127 | font-style: italic; 128 | } 129 | 130 | /** 131 | * Address variable `h1` font-size and margin within `section` and `article` 132 | * contexts in Firefox 4+, Safari, and Chrome. 133 | */ 134 | 135 | h1 { 136 | font-size: 2em; 137 | margin: 0.67em 0; 138 | } 139 | 140 | /** 141 | * Address styling not present in IE 8/9. 142 | */ 143 | 144 | mark { 145 | background: #ff0; 146 | color: #000; 147 | } 148 | 149 | /** 150 | * Address inconsistent and variable font size in all browsers. 151 | */ 152 | 153 | small { 154 | font-size: 80%; 155 | } 156 | 157 | /** 158 | * Prevent `sub` and `sup` affecting `line-height` in all browsers. 159 | */ 160 | 161 | sub, 162 | sup { 163 | font-size: 75%; 164 | line-height: 0; 165 | position: relative; 166 | vertical-align: baseline; 167 | } 168 | 169 | sup { 170 | top: -0.5em; 171 | } 172 | 173 | sub { 174 | bottom: -0.25em; 175 | } 176 | 177 | /* Embedded content 178 | ========================================================================== */ 179 | 180 | /** 181 | * Remove border when inside `a` element in IE 8/9/10. 182 | */ 183 | 184 | img { 185 | border: 0; 186 | } 187 | 188 | /** 189 | * Correct overflow not hidden in IE 9/10/11. 190 | */ 191 | 192 | svg:not(:root) { 193 | overflow: hidden; 194 | } 195 | 196 | /* Grouping content 197 | ========================================================================== */ 198 | 199 | /** 200 | * Address margin not present in IE 8/9 and Safari. 201 | */ 202 | 203 | figure { 204 | margin: 1em 40px; 205 | } 206 | 207 | /** 208 | * Address differences between Firefox and other browsers. 209 | */ 210 | 211 | hr { 212 | -moz-box-sizing: content-box; 213 | box-sizing: content-box; 214 | height: 0; 215 | } 216 | 217 | /** 218 | * Contain overflow in all browsers. 219 | */ 220 | 221 | pre { 222 | overflow: auto; 223 | } 224 | 225 | /** 226 | * Address odd `em`-unit font size rendering in all browsers. 227 | */ 228 | 229 | code, 230 | kbd, 231 | pre, 232 | samp { 233 | font-family: monospace, monospace; 234 | font-size: 1em; 235 | } 236 | 237 | /* Forms 238 | ========================================================================== */ 239 | 240 | /** 241 | * Known limitation: by default, Chrome and Safari on OS X allow very limited 242 | * styling of `select`, unless a `border` property is set. 243 | */ 244 | 245 | /** 246 | * 1. Correct color not being inherited. 247 | * Known issue: affects color of disabled elements. 248 | * 2. Correct font properties not being inherited. 249 | * 3. Address margins set differently in Firefox 4+, Safari, and Chrome. 250 | */ 251 | 252 | button, 253 | input, 254 | optgroup, 255 | select, 256 | textarea { 257 | color: inherit; /* 1 */ 258 | font: inherit; /* 2 */ 259 | margin: 0; /* 3 */ 260 | } 261 | 262 | /** 263 | * Address `overflow` set to `hidden` in IE 8/9/10/11. 264 | */ 265 | 266 | button { 267 | overflow: visible; 268 | } 269 | 270 | /** 271 | * Address inconsistent `text-transform` inheritance for `button` and `select`. 272 | * All other form control elements do not inherit `text-transform` values. 273 | * Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera. 274 | * Correct `select` style inheritance in Firefox. 275 | */ 276 | 277 | button, 278 | select { 279 | text-transform: none; 280 | } 281 | 282 | /** 283 | * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` 284 | * and `video` controls. 285 | * 2. Correct inability to style clickable `input` types in iOS. 286 | * 3. Improve usability and consistency of cursor style between image-type 287 | * `input` and others. 288 | */ 289 | 290 | button, 291 | html input[type="button"], /* 1 */ 292 | input[type="reset"], 293 | input[type="submit"] { 294 | -webkit-appearance: button; /* 2 */ 295 | cursor: pointer; /* 3 */ 296 | } 297 | 298 | /** 299 | * Re-set default cursor for disabled elements. 300 | */ 301 | 302 | button[disabled], 303 | html input[disabled] { 304 | cursor: default; 305 | } 306 | 307 | /** 308 | * Remove inner padding and border in Firefox 4+. 309 | */ 310 | 311 | button::-moz-focus-inner, 312 | input::-moz-focus-inner { 313 | border: 0; 314 | padding: 0; 315 | } 316 | 317 | /** 318 | * Address Firefox 4+ setting `line-height` on `input` using `!important` in 319 | * the UA stylesheet. 320 | */ 321 | 322 | input { 323 | line-height: normal; 324 | } 325 | 326 | /** 327 | * It's recommended that you don't attempt to style these elements. 328 | * Firefox's implementation doesn't respect box-sizing, padding, or width. 329 | * 330 | * 1. Address box sizing set to `content-box` in IE 8/9/10. 331 | * 2. Remove excess padding in IE 8/9/10. 332 | */ 333 | 334 | input[type="checkbox"], 335 | input[type="radio"] { 336 | box-sizing: border-box; /* 1 */ 337 | padding: 0; /* 2 */ 338 | } 339 | 340 | /** 341 | * Fix the cursor style for Chrome's increment/decrement buttons. For certain 342 | * `font-size` values of the `input`, it causes the cursor style of the 343 | * decrement button to change from `default` to `text`. 344 | */ 345 | 346 | input[type="number"]::-webkit-inner-spin-button, 347 | input[type="number"]::-webkit-outer-spin-button { 348 | height: auto; 349 | } 350 | 351 | /** 352 | * 1. Address `appearance` set to `searchfield` in Safari and Chrome. 353 | * 2. Address `box-sizing` set to `border-box` in Safari and Chrome 354 | * (include `-moz` to future-proof). 355 | */ 356 | 357 | input[type="search"] { 358 | -webkit-appearance: textfield; /* 1 */ 359 | -moz-box-sizing: content-box; 360 | -webkit-box-sizing: content-box; /* 2 */ 361 | box-sizing: content-box; 362 | } 363 | 364 | /** 365 | * Remove inner padding and search cancel button in Safari and Chrome on OS X. 366 | * Safari (but not Chrome) clips the cancel button when the search input has 367 | * padding (and `textfield` appearance). 368 | */ 369 | 370 | input[type="search"]::-webkit-search-cancel-button, 371 | input[type="search"]::-webkit-search-decoration { 372 | -webkit-appearance: none; 373 | } 374 | 375 | /** 376 | * Define consistent border, margin, and padding. 377 | */ 378 | 379 | fieldset { 380 | border: 1px solid #c0c0c0; 381 | margin: 0 2px; 382 | padding: 0.35em 0.625em 0.75em; 383 | } 384 | 385 | /** 386 | * 1. Correct `color` not being inherited in IE 8/9/10/11. 387 | * 2. Remove padding so people aren't caught out if they zero out fieldsets. 388 | */ 389 | 390 | legend { 391 | border: 0; /* 1 */ 392 | padding: 0; /* 2 */ 393 | } 394 | 395 | /** 396 | * Remove default vertical scrollbar in IE 8/9/10/11. 397 | */ 398 | 399 | textarea { 400 | overflow: auto; 401 | } 402 | 403 | /** 404 | * Don't inherit the `font-weight` (applied by a rule above). 405 | * NOTE: the default cannot safely be changed in Chrome and Safari on OS X. 406 | */ 407 | 408 | optgroup { 409 | font-weight: bold; 410 | } 411 | 412 | /* Tables 413 | ========================================================================== */ 414 | 415 | /** 416 | * Remove most spacing between table cells. 417 | */ 418 | 419 | table { 420 | border-collapse: collapse; 421 | border-spacing: 0; 422 | } 423 | 424 | td, 425 | th { 426 | padding: 0; 427 | } -------------------------------------------------------------------------------- /labnote/resources/css/skeleton.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Skeleton V2.0.4 3 | * Copyright 2014, Dave Gamache 4 | * www.getskeleton.com 5 | * Free to use under the MIT license. 6 | * http://www.opensource.org/licenses/mit-license.php 7 | * 12/29/2014 8 | */ 9 | 10 | 11 | /* Table of contents 12 | –––––––––––––––––––––––––––––––––––––––––––––––––– 13 | - Grid 14 | - Base Styles 15 | - Typography 16 | - Links 17 | - Buttons 18 | - Forms 19 | - Lists 20 | - Code 21 | - Tables 22 | - Spacing 23 | - Utilities 24 | - Clearing 25 | - Media Queries 26 | */ 27 | 28 | 29 | /* Grid 30 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 31 | .container { 32 | position: relative; 33 | width: 100%; 34 | max-width: 960px; 35 | margin: 0 auto; 36 | padding: 0 20px; 37 | box-sizing: border-box; } 38 | .column, 39 | .columns { 40 | width: 100%; 41 | float: left; 42 | box-sizing: border-box; } 43 | 44 | /* For devices larger than 400px */ 45 | @media (min-width: 400px) { 46 | .container { 47 | width: 85%; 48 | padding: 0; } 49 | } 50 | 51 | /* For devices larger than 550px */ 52 | @media (min-width: 550px) { 53 | .container { 54 | width: 80%; } 55 | .column, 56 | .columns { 57 | margin-left: 4%; } 58 | .column:first-child, 59 | .columns:first-child { 60 | margin-left: 0; } 61 | 62 | .one.column, 63 | .one.columns { width: 4.66666666667%; } 64 | .two.columns { width: 13.3333333333%; } 65 | .three.columns { width: 22%; } 66 | .four.columns { width: 30.6666666667%; } 67 | .five.columns { width: 39.3333333333%; } 68 | .six.columns { width: 48%; } 69 | .seven.columns { width: 56.6666666667%; } 70 | .eight.columns { width: 65.3333333333%; } 71 | .nine.columns { width: 74.0%; } 72 | .ten.columns { width: 82.6666666667%; } 73 | .eleven.columns { width: 91.3333333333%; } 74 | .twelve.columns { width: 100%; margin-left: 0; } 75 | 76 | .one-third.column { width: 30.6666666667%; } 77 | .two-thirds.column { width: 65.3333333333%; } 78 | 79 | .one-half.column { width: 48%; } 80 | 81 | /* Offsets */ 82 | .offset-by-one.column, 83 | .offset-by-one.columns { margin-left: 8.66666666667%; } 84 | .offset-by-two.column, 85 | .offset-by-two.columns { margin-left: 17.3333333333%; } 86 | .offset-by-three.column, 87 | .offset-by-three.columns { margin-left: 26%; } 88 | .offset-by-four.column, 89 | .offset-by-four.columns { margin-left: 34.6666666667%; } 90 | .offset-by-five.column, 91 | .offset-by-five.columns { margin-left: 43.3333333333%; } 92 | .offset-by-six.column, 93 | .offset-by-six.columns { margin-left: 52%; } 94 | .offset-by-seven.column, 95 | .offset-by-seven.columns { margin-left: 60.6666666667%; } 96 | .offset-by-eight.column, 97 | .offset-by-eight.columns { margin-left: 69.3333333333%; } 98 | .offset-by-nine.column, 99 | .offset-by-nine.columns { margin-left: 78.0%; } 100 | .offset-by-ten.column, 101 | .offset-by-ten.columns { margin-left: 86.6666666667%; } 102 | .offset-by-eleven.column, 103 | .offset-by-eleven.columns { margin-left: 95.3333333333%; } 104 | 105 | .offset-by-one-third.column, 106 | .offset-by-one-third.columns { margin-left: 34.6666666667%; } 107 | .offset-by-two-thirds.column, 108 | .offset-by-two-thirds.columns { margin-left: 69.3333333333%; } 109 | 110 | .offset-by-one-half.column, 111 | .offset-by-one-half.columns { margin-left: 52%; } 112 | 113 | } 114 | 115 | 116 | /* Base Styles 117 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 118 | /* NOTE 119 | html is set to 62.5% so that all the REM measurements throughout Skeleton 120 | are based on 10px sizing. So basically 1.5rem = 15px :) */ 121 | html { 122 | font-size: 62.5%; } 123 | body { 124 | font-size: 1.5em; /* currently ems cause chrome bug misinterpreting rems on body element */ 125 | line-height: 1.6; 126 | font-weight: 400; 127 | font-family: "Raleway", "HelveticaNeue", "Helvetica Neue", Helvetica, Arial, sans-serif; 128 | color: #222; } 129 | 130 | 131 | /* Typography 132 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 133 | h1, h2, h3, h4, h5, h6 { 134 | margin-top: 0; 135 | margin-bottom: 2rem; 136 | font-weight: 300; } 137 | h1 { font-size: 4.0rem; line-height: 1.2; letter-spacing: -.1rem;} 138 | h2 { font-size: 3.6rem; line-height: 1.25; letter-spacing: -.1rem; } 139 | h3 { font-size: 3.0rem; line-height: 1.3; letter-spacing: -.1rem; } 140 | h4 { font-size: 2.4rem; line-height: 1.35; letter-spacing: -.08rem; } 141 | h5 { font-size: 1.8rem; line-height: 1.5; letter-spacing: -.05rem; } 142 | h6 { font-size: 1.5rem; line-height: 1.6; letter-spacing: 0; } 143 | 144 | /* Larger than phablet */ 145 | @media (min-width: 550px) { 146 | h1 { font-size: 5.0rem; } 147 | h2 { font-size: 4.2rem; } 148 | h3 { font-size: 3.6rem; } 149 | h4 { font-size: 3.0rem; } 150 | h5 { font-size: 2.4rem; } 151 | h6 { font-size: 1.5rem; } 152 | } 153 | 154 | p { 155 | margin-top: 0; } 156 | 157 | 158 | /* Links 159 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 160 | a { 161 | color: #1EAEDB; } 162 | a:hover { 163 | color: #0FA0CE; } 164 | 165 | 166 | /* Buttons 167 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 168 | .button, 169 | button, 170 | input[type="submit"], 171 | input[type="reset"], 172 | input[type="button"] { 173 | display: inline-block; 174 | height: 38px; 175 | padding: 0 30px; 176 | color: #555; 177 | text-align: center; 178 | font-size: 11px; 179 | font-weight: 600; 180 | line-height: 38px; 181 | letter-spacing: .1rem; 182 | text-transform: uppercase; 183 | text-decoration: none; 184 | white-space: nowrap; 185 | background-color: transparent; 186 | border-radius: 4px; 187 | border: 1px solid #bbb; 188 | cursor: pointer; 189 | box-sizing: border-box; } 190 | .button:hover, 191 | button:hover, 192 | input[type="submit"]:hover, 193 | input[type="reset"]:hover, 194 | input[type="button"]:hover, 195 | .button:focus, 196 | button:focus, 197 | input[type="submit"]:focus, 198 | input[type="reset"]:focus, 199 | input[type="button"]:focus { 200 | color: #333; 201 | border-color: #888; 202 | outline: 0; } 203 | .button.button-primary, 204 | button.button-primary, 205 | input[type="submit"].button-primary, 206 | input[type="reset"].button-primary, 207 | input[type="button"].button-primary { 208 | color: #FFF; 209 | background-color: #33C3F0; 210 | border-color: #33C3F0; } 211 | .button.button-primary:hover, 212 | button.button-primary:hover, 213 | input[type="submit"].button-primary:hover, 214 | input[type="reset"].button-primary:hover, 215 | input[type="button"].button-primary:hover, 216 | .button.button-primary:focus, 217 | button.button-primary:focus, 218 | input[type="submit"].button-primary:focus, 219 | input[type="reset"].button-primary:focus, 220 | input[type="button"].button-primary:focus { 221 | color: #FFF; 222 | background-color: #1EAEDB; 223 | border-color: #1EAEDB; } 224 | 225 | 226 | /* Forms 227 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 228 | input[type="email"], 229 | input[type="number"], 230 | input[type="search"], 231 | input[type="text"], 232 | input[type="tel"], 233 | input[type="url"], 234 | input[type="password"], 235 | textarea, 236 | select { 237 | height: 38px; 238 | padding: 6px 10px; /* The 6px vertically centers text on FF, ignored by Webkit */ 239 | background-color: #fff; 240 | border: 1px solid #D1D1D1; 241 | border-radius: 4px; 242 | box-shadow: none; 243 | box-sizing: border-box; } 244 | /* Removes awkward default styles on some inputs for iOS */ 245 | input[type="email"], 246 | input[type="number"], 247 | input[type="search"], 248 | input[type="text"], 249 | input[type="tel"], 250 | input[type="url"], 251 | input[type="password"], 252 | textarea { 253 | -webkit-appearance: none; 254 | -moz-appearance: none; 255 | appearance: none; } 256 | textarea { 257 | min-height: 65px; 258 | padding-top: 6px; 259 | padding-bottom: 6px; } 260 | input[type="email"]:focus, 261 | input[type="number"]:focus, 262 | input[type="search"]:focus, 263 | input[type="text"]:focus, 264 | input[type="tel"]:focus, 265 | input[type="url"]:focus, 266 | input[type="password"]:focus, 267 | textarea:focus, 268 | select:focus { 269 | border: 1px solid #33C3F0; 270 | outline: 0; } 271 | label, 272 | legend { 273 | display: block; 274 | margin-bottom: .5rem; 275 | font-weight: 600; } 276 | fieldset { 277 | padding: 0; 278 | border-width: 0; } 279 | input[type="checkbox"], 280 | input[type="radio"] { 281 | display: inline; } 282 | label > .label-body { 283 | display: inline-block; 284 | margin-left: .5rem; 285 | font-weight: normal; } 286 | 287 | 288 | /* Lists 289 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 290 | ul { 291 | list-style: circle inside; } 292 | ol { 293 | list-style: decimal inside; } 294 | ol, ul { 295 | padding-left: 0; 296 | margin-top: 0; } 297 | ul ul, 298 | ul ol, 299 | ol ol, 300 | ol ul { 301 | margin: 1.5rem 0 1.5rem 3rem; 302 | font-size: 90%; } 303 | li { 304 | margin-bottom: 1rem; } 305 | 306 | 307 | /* Code 308 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 309 | code { 310 | padding: .2rem .5rem; 311 | margin: 0 .2rem; 312 | font-size: 90%; 313 | white-space: nowrap; 314 | background: #F1F1F1; 315 | border: 1px solid #E1E1E1; 316 | border-radius: 4px; } 317 | pre > code { 318 | display: block; 319 | padding: 1rem 1.5rem; 320 | white-space: pre; } 321 | 322 | 323 | /* Tables 324 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 325 | th, 326 | td { 327 | padding: 12px 15px; 328 | text-align: left; 329 | border-bottom: 1px solid #E1E1E1; } 330 | th:first-child, 331 | td:first-child { 332 | padding-left: 0; } 333 | th:last-child, 334 | td:last-child { 335 | padding-right: 0; } 336 | 337 | 338 | /* Spacing 339 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 340 | button, 341 | .button { 342 | margin-bottom: 1rem; } 343 | input, 344 | textarea, 345 | select, 346 | fieldset { 347 | margin-bottom: 1.5rem; } 348 | pre, 349 | blockquote, 350 | dl, 351 | figure, 352 | table, 353 | p, 354 | ul, 355 | ol, 356 | form { 357 | margin-bottom: 2.5rem; } 358 | 359 | 360 | /* Utilities 361 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 362 | .u-full-width { 363 | width: 100%; 364 | box-sizing: border-box; } 365 | .u-max-full-width { 366 | max-width: 100%; 367 | box-sizing: border-box; } 368 | .u-pull-right { 369 | float: right; } 370 | .u-pull-left { 371 | float: left; } 372 | 373 | 374 | /* Misc 375 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 376 | hr { 377 | margin-top: 3rem; 378 | margin-bottom: 3.5rem; 379 | border-width: 0; 380 | border-top: 1px solid #E1E1E1; } 381 | 382 | 383 | /* Clearing 384 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 385 | 386 | /* Self Clearing Goodness */ 387 | .container:after, 388 | .row:after, 389 | .u-cf { 390 | content: ""; 391 | display: table; 392 | clear: both; } 393 | 394 | 395 | /* Media Queries 396 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 397 | /* 398 | Note: The best way to structure the use of media queries is to create the queries 399 | near the relevant code. For example, if you wanted to change the styles for buttons 400 | on small devices, paste the mobile query code up in the buttons section and style it 401 | there. 402 | */ 403 | 404 | 405 | /* Larger than mobile */ 406 | @media (min-width: 400px) {} 407 | 408 | /* Larger than phablet (also point when grid becomes active) */ 409 | @media (min-width: 550px) {} 410 | 411 | /* Larger than tablet */ 412 | @media (min-width: 750px) {} 413 | 414 | /* Larger than desktop */ 415 | @media (min-width: 1000px) {} 416 | 417 | /* Larger than Desktop HD */ 418 | @media (min-width: 1200px) {} 419 | -------------------------------------------------------------------------------- /labnote/resources/img/divider-line1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/khughitt/labnote/0c7815936f955f3609628f8a5cab28b6ed2451c4/labnote/resources/img/divider-line1.png -------------------------------------------------------------------------------- /labnote/resources/img/hr-jon-lucas2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/khughitt/labnote/0c7815936f955f3609628f8a5cab28b6ed2451c4/labnote/resources/img/hr-jon-lucas2.jpg -------------------------------------------------------------------------------- /labnote/resources/img/libreoffice-impress.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/khughitt/labnote/0c7815936f955f3609628f8a5cab28b6ed2451c4/labnote/resources/img/libreoffice-impress.png -------------------------------------------------------------------------------- /labnote/templates/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/khughitt/labnote/0c7815936f955f3609628f8a5cab28b6ed2451c4/labnote/templates/__init__.py -------------------------------------------------------------------------------- /labnote/templates/default.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{ author }} - {{ title }} 5 | 6 | 7 | 8 | 9 | 10 | 11 | {% if user_css != '' -%} 12 | 13 | {% endif -%} 14 | {% if user_js != '' -%} 15 | 16 | {% endif -%} 17 | 18 | 19 |
20 | 21 | 22 | 27 |
28 | 29 | 30 | {% for category in entries %} 31 | 32 | 33 |
34 | 35 |
36 | {% if entries[category].image != '' and loop.index % 2 == 1 -%} 37 |
38 | 39 |
40 | {% else -%} 41 |   42 | {% endif -%} 43 |
44 | 45 | 46 |
47 |

{{ category }}

48 |
    49 | {% for entry in entries[category] -%} 50 | {% if entry["date"].timestamp() > 0 -%} 51 | {% set entry_time = entry["date"].strftime("%Y/%m/%d") -%} 52 | {% else -%} 53 | {% set entry_time = "-" -%} 54 | {% endif -%} 55 |
  • 56 | 57 | {{ entry["title"] }} 58 |
  • 59 | {% endfor -%} 60 |
61 |
62 | 63 | 64 |
65 | {% if entries[category].image != '' and loop.index % 2 == 0 -%} 66 |
67 | 68 |
69 | 70 | {% else -%} 71 |   72 | {% endif -%} 73 |
74 |
75 | {% endfor -%} 76 | 77 |
78 | 79 | 80 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [tool:pytest] 2 | norecursedirs = 'tox' 'build' 3 | 4 | [aliases] 5 | test=pytest 6 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | """ 2 | labnote - HTML lab notebook generator 3 | """ 4 | DOCLINES = __doc__.split("\n") 5 | 6 | from setuptools import setup, find_packages 7 | 8 | CLASSIFIERS = [ 9 | 'Development Status :: 4 - Beta', 10 | 'Intended Audience :: Science/Research', 11 | 'License :: OSI Approved :: BSD License', 12 | 'Programming Language :: Python', 13 | 'Programming Language :: Python :: 3', 14 | 'Topic :: Scientific/Engineering', 15 | 'Topic :: Scientific/Engineering :: Bio-Informatics', 16 | 'Operating System :: Microsoft :: Windows', 17 | 'Operating System :: POSIX', 18 | 'Operating System :: Unix', 19 | 'Operating System :: MacOS' 20 | ] 21 | 22 | with open('README.rst',encoding='utf8') as file: 23 | long_description = file.read() 24 | 25 | setup( 26 | author="Keith Hughitt", 27 | author_email="khughitt@umd.edu", 28 | classifiers=CLASSIFIERS, 29 | description="Flexible and lightweight tool for generating HTML-based electronic lab notebooks", 30 | long_description=long_description, 31 | install_requires=['beautifulsoup4', 'jinja2', 'PyYAML', 'setuptools-git'], 32 | setup_requires=['pytest-runner'], 33 | tests_require=['pytest>=2.8'], 34 | include_package_data=True, 35 | license="BSD", 36 | maintainer="Keith Hughitt", 37 | maintainer_email="khughitt@umd.edu", 38 | name="labnote", 39 | packages=find_packages(), 40 | platforms=["Linux", "Solaris", "Mac OS-X", "Unix", "Windows"], 41 | provides=['labnote'], 42 | scripts=['bin/labnote'], 43 | zip_safe=False, 44 | url="https://github.com/khughitt/labnote", 45 | download_url="https://github.com/khughitt/labnote/tarball/0.9.3", 46 | version="0.9.3" 47 | ) 48 | 49 | -------------------------------------------------------------------------------- /tests/conftest.py: -------------------------------------------------------------------------------- 1 | """ 2 | Labnote Notebook Fixture 3 | 4 | Note: When running tests in a virtualenv container, be sure to install py.test 5 | in the virtualenv so that system version isn't used instead. 6 | """ 7 | import os 8 | import pytest 9 | import time 10 | from labnote import Notebook 11 | 12 | @pytest.fixture(scope="session") 13 | def nb1(tmpdir_factory): 14 | conf = 'tests/notebooks/nb1/config.yml' 15 | outfile = str(tmpdir_factory.mktemp('output').join('index.html')) 16 | 17 | # touch files to update timestamp: 18 | # since the entry 'date' fields are set at the time of notebook 19 | # construction, it's easier to start with the entries date-sorted, and 20 | # then resort by title to test both sort types. 21 | base_dir = 'tests/notebooks/nb1/foo' 22 | for x in sorted(os.listdir(base_dir)): 23 | # Python 3.4+ 24 | #pathlib.Path(os.path.join(base_dir, x)).touch() 25 | os.utime(os.path.join(base_dir, x), None) 26 | time.sleep(1) 27 | 28 | base_dir = 'tests/notebooks/nb1/bar' 29 | for x in sorted(os.listdir(base_dir)): 30 | os.utime(os.path.join(base_dir, x), None) 31 | time.sleep(1) 32 | 33 | return Notebook(conf, output_file=outfile) 34 | 35 | @pytest.fixture(scope="session") 36 | def nb1_alphanum(tmpdir_factory): 37 | conf = 'tests/notebooks/nb1/config.yml' 38 | outfile = str(tmpdir_factory.mktemp('output').join('index.html')) 39 | 40 | return Notebook(conf, output_file=outfile, 41 | sort_entries_by_date=False, sort_categories_by_date=False) 42 | -------------------------------------------------------------------------------- /tests/notebooks/nb1/bar/one: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/khughitt/labnote/0c7815936f955f3609628f8a5cab28b6ed2451c4/tests/notebooks/nb1/bar/one -------------------------------------------------------------------------------- /tests/notebooks/nb1/bar/three: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/khughitt/labnote/0c7815936f955f3609628f8a5cab28b6ed2451c4/tests/notebooks/nb1/bar/three -------------------------------------------------------------------------------- /tests/notebooks/nb1/bar/two: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/khughitt/labnote/0c7815936f955f3609628f8a5cab28b6ed2451c4/tests/notebooks/nb1/bar/two -------------------------------------------------------------------------------- /tests/notebooks/nb1/config.yml: -------------------------------------------------------------------------------- 1 | categories: 2 | First: foo 3 | Second: bar 4 | include_files: '*' 5 | input_dirs: 'tests/notebooks/nb1/*' 6 | output_file: 'tests/notebooks/nb1/index.html' 7 | sort_entries_by_date: true 8 | sort_categories_by_date: true 9 | -------------------------------------------------------------------------------- /tests/notebooks/nb1/foo/1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/khughitt/labnote/0c7815936f955f3609628f8a5cab28b6ed2451c4/tests/notebooks/nb1/foo/1 -------------------------------------------------------------------------------- /tests/notebooks/nb1/foo/2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/khughitt/labnote/0c7815936f955f3609628f8a5cab28b6ed2451c4/tests/notebooks/nb1/foo/2 -------------------------------------------------------------------------------- /tests/notebooks/nb1/foo/3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/khughitt/labnote/0c7815936f955f3609628f8a5cab28b6ed2451c4/tests/notebooks/nb1/foo/3 -------------------------------------------------------------------------------- /tests/notebooks/nb1/foo/a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/khughitt/labnote/0c7815936f955f3609628f8a5cab28b6ed2451c4/tests/notebooks/nb1/foo/a -------------------------------------------------------------------------------- /tests/notebooks/nb1/foo/b: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/khughitt/labnote/0c7815936f955f3609628f8a5cab28b6ed2451c4/tests/notebooks/nb1/foo/b -------------------------------------------------------------------------------- /tests/notebooks/nb1/foo/c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/khughitt/labnote/0c7815936f955f3609628f8a5cab28b6ed2451c4/tests/notebooks/nb1/foo/c -------------------------------------------------------------------------------- /tests/notebooks/nb1/other/misc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/khughitt/labnote/0c7815936f955f3609628f8a5cab28b6ed2451c4/tests/notebooks/nb1/other/misc -------------------------------------------------------------------------------- /tests/test_notebook.py: -------------------------------------------------------------------------------- 1 | """ 2 | Notebook test code 3 | """ 4 | class TestNotebook(): 5 | def test_category_date_sorting(self, nb1): 6 | e = nb1.entries 7 | 8 | # Expected 9 | expected = { 10 | 'categories': ['Second', 'First', 'Other'], 11 | 'descriptions': ['', '', ''], 12 | 'images': ['', '', ''], 13 | 'patterns': [['bar'], ['foo'], []] 14 | } 15 | 16 | # categories 17 | assert list(e.keys()) == expected['categories'] 18 | 19 | # category descriptions 20 | descriptions = [e[cat].description for cat in e] 21 | assert descriptions == expected['descriptions'] 22 | 23 | # category images 24 | images = [e[cat].image for cat in e] 25 | assert images == expected['images'] 26 | 27 | # category patterns 28 | patterns = [e[cat].patterns for cat in e] 29 | assert patterns == expected['patterns'] 30 | 31 | def test_entry_date_sorting(self, nb1): 32 | """Tests labnote date-sorting (default for nb1)""" 33 | # files were touched in alphanumeric order, so they should appear in 34 | # reverse-alphanumeric order 35 | expected_titles = { 36 | 'First': ['c', 'b', 'a', '3', '2', '1'], 37 | 'Other': ['misc'], 38 | 'Second': ['two', 'three', 'one'] 39 | } 40 | 41 | e = nb1.entries 42 | 43 | # check order of entries in each category 44 | for category in e: 45 | titles = [item.title for item in e[category]] 46 | assert titles == expected_titles[category] 47 | 48 | def test_entry_alphanum_sorting(self, nb1_alphanum): 49 | """Tests labnote alphanumeric sorting""" 50 | e = nb1_alphanum.entries 51 | 52 | # Sorted alphanumerically 53 | expected_titles = { 54 | 'First': ['1', '2', '3', 'a', 'b', 'c'], 55 | 'Second': ['one', 'three', 'two'], 56 | 'Other': ['misc'], 57 | } 58 | 59 | # check order of entries in each category 60 | for category in e: 61 | titles = [item.title for item in e[category]] 62 | assert titles == expected_titles[category] 63 | 64 | def test_entry(self, nb1): 65 | """Checks the values for a single entry""" 66 | import os 67 | 68 | e = nb1.entries['Second'][0] 69 | 70 | assert e.dir_name == 'bar' 71 | assert e.filename == 'two' 72 | assert os.path.normpath(e.filepath) == os.path.normpath('tests/notebooks/nb1/bar/two') 73 | assert e.title == 'two' 74 | assert e.url == 'tests/notebooks/nb1/bar/two' 75 | 76 | -------------------------------------------------------------------------------- /tests/test_renderer.py: -------------------------------------------------------------------------------- 1 | """ 2 | NotebookRenderer test code 3 | """ 4 | import os 5 | 6 | class TestNotebook(): 7 | def test_notebook_render(self, nb1): 8 | # Render notebook to HTML and check for output file 9 | nb1.render() 10 | assert os.path.exists(nb1.output_file) 11 | 12 | --------------------------------------------------------------------------------