Docs Developer Guide
This document is for contributors working on the design, templating, deployment, or development of the ODK Docs website.
Tech Overview
ODK Docs uses:
Sphinx, a static-site generator written in Python
Sphinx uses:
Docutils for parsing reStructuredText
Jinja for templating
sphinx_rtd_theme, a Sphinx theme/template
sphinx_rtd_theme uses:
JQuery, a JavaScript library
Proselint for style testing
CircleCI for testing and deployment
Amazon S3 for hosting
Custom HTML templating
ODK Docs uses the sphinx_rtd_theme, with some minor customizations.
ODK-specific versions of HTML/Jinja templates
are in _templates
.
Any file in that directory
will override the file of the same name
in the sphinx_rtd_theme source.
So, to customize a portion of the HTML template, copy the source file from sphinx_rtd_theme and then edit it.
Please commit the copied file unchanged before editing, so that it is easy to track what you have changed.
Custom JavaScript
Custom JavaScript should be added in src/_static/js/custom.js
.
Comment your code with an explanation of what the JS accomplishes,
and a reference to the issue number you are working on.
The ODK Docs template includes JQuery, so you can use it in your custom JS.
Custom CSS
Custom CSS should be added in src/_static/css/custom.css
.
Comment your code with an explanation of what the CSS accomplishes
and a reference to the issue number you are working on.
For example:
/* Example CSS PR #xyx */
div[class^='example'] {
color: black;
}
It is helpful to keep the CSS file organized.
There are several sections in the custom.css
file:
Styling for rst roles and directives
Responsive CSS
Styling for JS implementation
Utility classes
Each of these sections are enclosed in start and end comments. Add your code to the relevant section. If you don't find any section relevant, add a new section and add your code there.
For example:
/* New section starts */
/* Example CSS PR #xyx */
div[class^='example'] {
color: black;
}
/* New section ends */
Style Guide checks
Proselint is used for style testing the docs. Apart from the built-in tests in proselint, custom checks are added for style guide testing. Following a literate programming. model, style checks are defined in docs-style-guide.rst. After each style rule, you can define a python code-block containing the code for style testing. When the style-test script is run, these python code-blocks are parsed to generate a testing script.
Proselint dependent checks
In most of the custom checks, a new function is written that calls one of the built-in proselint functions as a return value.
All the checks use a decorator memoize()
to cache the check for faster execution.
- memoize()
Use
@memoize
above function definition to cache the result.
Proselint provides several functions for defining style tests:
- existence_check(text, list, err, msg, ignore_case=True, str=False, max_errors=float('inf'), offset=0, require_padding=True, dotall=False, excluded_topics=None, join=False)
To check for existence of a regex pattern(s) in the text. The parameters
offset
,excluded_topics
andjoin
are not needed for style guide testing.- Parameters:
text (str) – Text to be checked
list (list) – List of regex expressions
err (str) – Name of the test
msg (str) – Error or warning message
ignore_case (bool) – For using
re.IGNORECASE
str (bool) – For using
re.UNICODE
max_errors (float) – Maximum number of errors to be generated
require_padding (bool) – To use padding with the specified regex (It is better to set it as False and specify the regex accordingly)
dotall (bool) – For using
re.DOTALL
- Returns:
The error list consisting of error tuples:
[(start, end, err, msg, replacement)]
.- Return type:
list
- preferred_forms_check(text, list, err, msg, ignore_case=True, offset=0, max_errors=float('inf'))
To suggest a preferred form of the word used. The parameter
offset
is not needed for style guide testing.- Parameters:
text (str) – Text to be checked
list (list) – list of comparison (words or regex):
[correct form , incorrect form]
err (str) – Name of the test
msg (str) – Error or warning message
ignore_case (bool) – For using
re.IGNORECASE
max_errors (float) – Maximum number of errors to be generated
- Returns:
The error list consisting of error tuples:
[(start, end, err, msg, replacement)]
.- Return type:
list
- consistency_check(text, word_pairs, err, msg, offset=0)
To check for consistency for the given word pairs. The parameters
offset
is not needed for style guide testing.- Parameters:
text (str) – Text to be checked
word_pairs (list) – Word pairs to be checked for consistency
err (str) – Name of the test
msg (str) – Error or warning message
- Returns:
The error list consisting of error tuples:
[(start, end, err, msg, replacement)]
.- Return type:
list
Note
The checker functions are used by the
built-in proselint function lint()
to generate an error list of different format.
The returned list finally is: [(check, message, line, column, start, end, end - start, "warning", replacements)]
See also
Example Usage
@memoize
def example(text):
"""Example check."""
err = "style-guide.example"
msg = "A demonstration for writing checks."
regex = "[\.\?!](example)"
return existence_check(text, [regex], err, msg, ignore_case=False,
require_padding=False)
When you define code-blocks which use built-in proselint testing, specify the class style-checks.
.. code-block:: python
:class: style-checks
The generated file after parsing code
for style checks is style-checks.py
.
If the test is too large
to be defined in the file docs-style-guide.rst
,
you can use a snippet from the test
(as in this Docs Style Guide example).
The code-blocks for such snippets
should specify the class proselint-extra-checks.
Define the complete test
in the file /style-guide/proselint-extra-checks.py
.
Independent checks
Apart from the checks, which are to be run through proselint,
you can add extra checks to be run independently.
They are not enabled in proselintrc
as well.
For example, the checks for finding quote marks and section labels
do not use any built-in functions to obtain an error list.
Example Usage
def check_quotes(text):
"""Avoid using straight quotes."""
err = "style-guide.check-quote"
msg = "Avoid using quote marks."
regex = r"\"[a-zA-z0-9 ]{1,15}\""
errors = []
for matchobj in re.finditer(regex, text):
start = matchobj.start()+1
end = matchobj.end()
(row, col) = line_and_column(text, start)
extent = matchobj.end()-matchobj.start()
errors += [(err, msg, row, col, start, end,
extent, "warning", "None")]
return errors
The code-blocks for extra checks
should specify the class extra-checks.
The generated file after parsing code
for extra checks is extra-checks.py
.
Note
Built-in proselint function line_and_column()
is used with extra checks to obtain the row and column of the matched text.
- line_and_column(text, start)
To find the line number and column of a position in a string.
- Parameters:
text (str) – Text to be searched for
start (int) – Starting position of matched pattern
- Returns:
Tuple containing row and column number
- Return type:
tuple
Error vs warning
Warnings are intended to provide guidance to authors.
Errors enforce "hard" rules, and raising an error will stop the build.
You can classify the result of a check
as an error if you are sure
that no false positives would be produced.
The checks classified as errors should return a replacement
for fixing the errors.
Proselint dependent checks which use the function
preferred_forms_check()
or consistency_check()
always return a preferred form.
If you create an independent check
which generates an error
make sure to return a replacement in the error list.
To generate an error from a check,
specify the check name in the list of errors
in the function get_errlist()
in the file style-test.py
.
Excluding built-in proselint checks
To exclude a built-in proselint check,
specify the check name in the check list
in the function exclude_checks()
in the file style-test.py
.