Commit b3379139 by Szeberényi Imre

Initial commit

parents
.*.sw*
*.pkl
*~
.DS_Store
MANIFEST
build
dist
docs/_build
docs/index.html
README.html
docs/apidocs
docs/html
docs/pdf
src
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
# C extensions
*.so
# Distribution / packaging
eggs/
lib/
lib64/
parts/
sdist/
var/
*.egg-info/
.installed.cfg
*.egg
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
.tox/
.coverage
.cache
nosetests.xml
coverage.xml
# Translations
*.mo
#PyCharm
.idea
*.pem
web: gunicorn mit_lti_flask_sample:app --log-file -
Sample LTI Provider for Flask
=============================
|Deploy|
.. |Deploy| image:: https://www.herokucdn.com/deploy/button.png
:target: https://heroku.com/deploy
This is a sample LTI provider for the Flask framework [#f1]_. It is a minimal
implementation that provides a starting point for a custom LTI provider.
It is one of a series of LTI providers written for popular frameworks and
using the Python LTI library, PyLTI. Additional sample LTI providers for
other Python frameworks are planned. Check for them on the PyLTI Github site,
`https://github.com/mitodl/pylti
<https://github.com/mitodl/pylti>`_. While these LTI provider examples can
be used with any LTI consumer, they were created for use with edX. Integrating
an LTI provider with edX is described in the edX LTI `docs.
<http://edx.readthedocs.org/projects/edx-partner-course-staff/en/latest/exercises_tools/lti_component.html>`_
You will need both this app and the PyLTI library to create your own LTI
provider. Each sample contains only the code variations necessary to support
its specific framework. By creating an interface boundary between a sample
provider and PyLTI, PyLTI manages the specific LTI features and each sample
manages the specific requirements of its framework. You can easily switch your
custom provider from one framework to another.
If you have questions or problems, please create an issue on the
project's GitHub site,
`https://github.com/mitodl/mit_lti_flask_sample/issues
<https://github.com/mitodl/mit_lti_flask_sample/issues>`_
Please see the PyLTI README `https://github.com/mitodl/pylti
<https://github.com/mitodl/pylti>`_ for a detailed description of the architecture.
Super Quick Start
-----------------
You can try out the sample app for free by deploying it to your Heroku account
simply by clicking the deploy button:
|Deploy|
.. |Deploy| image:: https://www.herokucdn.com/deploy/button.png
:target: https://heroku.com/deploy
After deployment is complete you can customize the app from the Heroku
git repository that is created. This button will also work in forked
repositories with your own modifications.
Quick Start
-----------
Open your terminal and navigate to the directory you want to contain your
working directory. Execute these commands:
.. code-block:: bash
git clone git@github.com:mitodl/mit_lti_flask_sample.git
cd mit_lti_flask_sample
pip install -r requirements.txt
python mit-lti-flask-sample.py
Then navigate to `http://localhost:5000/is_up <http://localhost:5000/is_up>`_
If you see a page containing the words, "I'm up", you have verified that you
can run the sample app locally.
1. Deploy the sample app to a server accessible from your LTI consumer, edX or
another LMS. (These instructions presume you're using edX, but they are
similar for any LTI consumer.)
#. In edX Studio, navigate to ``Settings\Advanced Settings`` and enter these
values for the specified keys.
======================= ========================
Keys Values
======================= ========================
Advanced Module List ``[ lti ]``
----------------------- ------------------------
LTI Passports ``[ "lti_starx_add_demo:__consumer_key__:__lti_secret__" ]``
======================= ========================
3. Still in edX Studio, navigate to the content page that will contain your LTI
tool and create an LTI Advanced Component.
#. Enter the LTI ID for the external LTI provider.
#. Enter the URL of the external tool that this component launches.
======================= ========================
Keys Values
======================= ========================
LTI ID ``lti_starx_add_demo``
----------------------- ------------------------
LTI URL ``THE_URL_OF_YOUR_DEPLOYED_LTI_PROVIDER``
======================= ========================
.. [#f1] From their website, *"Flask is a microframework for Python based on
Werkzeug, Jinja 2 and good intentions."* `http://flask.pocoo.org/ <http://flask.pocoo.org/>`_
There is more comprehensive documentation of these settings at
`http://edx-partner-course-staff.readthedocs.org/en/latest/exercises_tools/lti_component.html
<http://edx-partner-course-staff.readthedocs.org/en/latest/exercises_tools/lti_component.html>`_
{
"name": "MIT Flask LTI Provider",
"description": "This is a sample LTI provider for the Flask framework. It is a minimal implementation that provides a starting point.",
"keywords": [
"flask",
"LTI",
"Learning Tools Interoperability",
"Open edX"
],
"website": "http://mit-lti-flask-example.readthedocs.org/en/latest/",
"repository": "https://github.com/mitodl/mit_lti_flask_sample",
"success_url": "/is_up",
"env": {
"FLASK_SECRET_KEY": {
"description": "The standard flask secret key for session storage security",
"generator": "secret"
},
"CONSUMER_KEY_SECRET": {
"description": "OAuth1 Secret for LTI authentication to match `__consumer_key__` consumer name",
"generator": "secret"
},
"CONSUMER_KEY_PEM_FILE": {
"description": "If you need to do SSL authentication on top of regular OAUTH, paste in your plain text base64 encoded SSL certificate and private key",
"required": false
}
}
}
-----BEGIN CERTIFICATE-----
MIIDBjCCAe4CCQCz/R+NaGyiWTANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJB
VTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0
cyBQdHkgTHRkMB4XDTE0MDYyNDE3MjkzN1oXDTE1MDYyNDE3MjkzN1owRTELMAkG
A1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGEludGVybmV0
IFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
AMDYqHJVGBjexc0+SHI8xtYRjCUDngoSnEMzylFi22Muljyfq9gI7El064Pb8ugJ
UreRCTRceVMF4k+9/kUTzkB3ofsKxLoRIXTEs9gMqb+znuBBfh5VU3hyKcPH93Re
IgyomkwdDfjPsmvtYvP2lTghWrfsZ4aTHuqPl8CYAzIWYhshzc/UVex5aq0KHuwy
SMAy9fZLJtOXZ/cIooPijbKicJP03GmpXe2l5bvSKobWlAyYv7Vq/BsrKr3yGWk+
Uz/8bP2VJDpbm1EHJ6ePFEAuPQc6IvoOaDpNsXNY7c1stDDxkQ/pla3V/GgdLoCP
dFTZkbPkMieWTOK6s4wH44MCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAueT+gJyP
M5SztSpUQG8nOyKxoE82yhCTGcsC+DxtKtHdwlyc/cDDrpowA/WYmKWOkG/sLsI7
kHZy2KtXdUopCxiBQ8QBpJ3DTm0JSXdiyPNqmO5p0bZ3b3FaI7EvI95eBdQR8XpI
YS3vLPONl5waOv4sULDCFGAK3dqVbMztoGgZD4lCMkEC86y3t1XUBWoCHNf1krDW
F0klkZWnO8qWOkSZYj4ixiSE+IFXQ9ePRluhNmC0Py/JHzNFJCNuJ9HN7EQ4qwBv
OyxGKbWkin+AH9lGn7Ir6ltemtyqXDf+U7NORCslieXVshuA75N5Rjme+BEai87p
KUcTsOI01V9Dtw==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE REQUEST-----
MIICijCCAXICAQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUx
ITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZIhvcN
AQEBBQADggEPADCCAQoCggEBAMDYqHJVGBjexc0+SHI8xtYRjCUDngoSnEMzylFi
22Muljyfq9gI7El064Pb8ugJUreRCTRceVMF4k+9/kUTzkB3ofsKxLoRIXTEs9gM
qb+znuBBfh5VU3hyKcPH93ReIgyomkwdDfjPsmvtYvP2lTghWrfsZ4aTHuqPl8CY
AzIWYhshzc/UVex5aq0KHuwySMAy9fZLJtOXZ/cIooPijbKicJP03GmpXe2l5bvS
KobWlAyYv7Vq/BsrKr3yGWk+Uz/8bP2VJDpbm1EHJ6ePFEAuPQc6IvoOaDpNsXNY
7c1stDDxkQ/pla3V/GgdLoCPdFTZkbPkMieWTOK6s4wH44MCAwEAAaAAMA0GCSqG
SIb3DQEBBQUAA4IBAQAg7SpRe2mbjfxPBqWOgAxwMdB9htb/PrvERZoi5T4NNnL9
oBrF/L4bbBSlrLa7ENEr3e9bJ5PT4Von4I4mJGXE1tqZ3+gdgTGSVCIJDcHlIg+n
As+NGf8Z75lyieQm9csS9xFR+ki82Yz7Rza0iFWVyyEYrNMt3vxsrnzMkOM+xWrD
whvk/qjHt4H2WNu9KmSCc3pHZVnHAYFYnqhnq6XkaWpMBNJmEKcQqmjEFFBqRCYO
aGTtkuVMt4PAxZENcAw5jzZrlNZtxr16CZ6ltrJidsG+UYAZNgzNE1yeSns380x6
2evAR/LDy8pREeP34DTUBU7aPGemeNyiaNNUVv3j
-----END CERTIFICATE REQUEST-----
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAwNioclUYGN7FzT5IcjzG1hGMJQOeChKcQzPKUWLbYy6WPJ+r
2AjsSXTrg9vy6AlSt5EJNFx5UwXiT73+RRPOQHeh+wrEuhEhdMSz2Aypv7Oe4EF+
HlVTeHIpw8f3dF4iDKiaTB0N+M+ya+1i8/aVOCFat+xnhpMe6o+XwJgDMhZiGyHN
z9RV7HlqrQoe7DJIwDL19ksm05dn9wiig+KNsqJwk/Tcaald7aXlu9IqhtaUDJi/
tWr8GysqvfIZaT5TP/xs/ZUkOlubUQcnp48UQC49Bzoi+g5oOk2xc1jtzWy0MPGR
D+mVrdX8aB0ugI90VNmRs+QyJ5ZM4rqzjAfjgwIDAQABAoIBAA7PVLhbEpuuWBB4
DQZA5qA//rxE99kcLsX2bz4q0mBejokfVgokPUky4ndg59msBZ81UzijNWoL1KJ9
G87gQ4Gt7lGiNRN5B1G5XXplFpAzUis0vGPF98ATPGPdsoJMTF8ef6MCxDTsdm5V
b7j+od0KqrXfQOAT4lbvKh9PTUeGUBf2JAGF7wTYyKl8ca7klzOmCKvUHDQ4haJe
i70g1fHOt0nVLKpS5hbL+V22HZ1j2cO6xonTrmsu3BHgpzICakKUqRi0IqBzeDpI
QeZwo+kGtvC8921a0Fgx/24zr8tXNKlzdfCfupCDM1dopYUdQ4N8PbAKdnjprb3M
Aias/YkCgYEA/ldlJrLTsoOQ1SOK77K9vMU8js6Bpq2ZulY3ii30p5tAJEPBnmwu
7yeBwoFnosORt108jbbDx4kIRNy/duHqg4D6oPuXVQk294GLz9CqXWuUY+xgaIG0
XzTP+QzUiAIjELjP9L6om4E3XY5UMzTX07J43FU6RO/0INghgjhd0Z8CgYEAwhqZ
6gRWHRaewRAuGSHXHfLc9FyYajWEuvXNRlVkNJfVZizgdepgHWgXAZ91gBt+r9gT
MP9nqdHn7vJnbaWciKNXQ+1hLm5T9cP2ONPdbDrBbBRlPaH/TwKgEp+hvxxDe90K
MXJ8LHeA+Vuxb2FYerPJLMjpYREsIjNNdVkzi50CgYEA5z0N+eEcF3EGfMGTz9ny
J24rYKrftWqgGYnJdRfTF49PAamYnzA8e7KvhuegEZs9IDjklPNVjFRJ8FHCwZMk
Y+sf8rzLGcUG9IATXR2ZxtXIVBDUli9Wjji13PiNQhqvRsTATQQKx1bzv1tnsiaZ
Ex9wQuI51/NRQYs+6+XTZoECgYAO76MWWd0SgfIXYxpY/ryh5lVQTGvCCHo9NLrm
BZ4fWeum4B8pk65vtNnIWy/cesEl4NehNpYQgEF7ppWE33CLJx6I8JRA0WaOi9S/
jLYGJQNLQMqFkpilyMrug/AbV55twpWNNoUNmuiGx6KAspnvXzREyPUEbiDaRN5a
AFIyFQKBgQDLvIJNdFRd/RdaTfNQ1Bzip06uoPZe6XzJGpiHZNVEMt5vB3R2s7k0
94KlDsF3z5/uuWPwnnfmxdRSw8zdpfL6GGxRnz8zzYJ646QpNL/IZk/iuDL2y3BB
wgCP6mzRn0hCEdysyeuTYIYwJuobnj0TKMxySeRx2Xk2k5a1iInswg==
-----END RSA PRIVATE KEY-----
"""
Configuration file for flask sample application
"""
import os
# enable CSRF
WTF_CSRF_ENABLED = True
# secret key for authentication
SECRET_KEY = os.environ.get("FLASK_SECRET_KEY", "you-will-never-guess")
# Sample client certificate example for 12 factor app
# You would want to store your entire pem in an environment variable
# with something like:
# ```
# export CONSUMER_KEY_CERT=$(cat <<EOF
# < paste cert here>
# EOF
# )
# ```
CONSUMER_KEY_PEM_FILE = os.path.abspath('consumer_key.pem')
with open(CONSUMER_KEY_PEM_FILE, 'w') as wfile:
wfile.write(os.environ.get('CONSUMER_KEY_CERT', ''))
PYLTI_CONFIG = {
"consumers": {
"__consumer_key__": {
"secret": os.environ.get("CONSUMER_KEY_SECRET", "__lti_secret__"),
# "cert": CONSUMER_KEY_PEM_FILE
}
}
}
# Remap URL to fix edX's misrepresentation of https protocol.
# You can add another dict entry if you have trouble with the
# PyLti URL.
PYLTI_URL_FIX = {
"https://localhost:8000/": {
"https://localhost:8000/": "http://localhost:8000/"
},
"https://localhost/": {
"https://localhost/": "http://192.168.33.10/"
}
}
# Makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
SPHINXAPIDOC = sphinx-apidoc
PAPER =
BUILDDIR = _build
# User-friendly check for sphinx-build
ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)
$(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/)
endif
# User-friendly check for sphinx-apidoc
ifeq ($(shell which $(SPHINXAPIDOC) >/dev/null 2>&1; echo $$?), 1)
$(error The '$(SPHINXAPIDOC)' command was not found. Make sure you have Sphinx installed, then set the SPHINXAPIDOC 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/)
endif
# Internal variables.
PAPEROPT_a4 = -D latex_paper_size=a4
PAPEROPT_letter = -D latex_paper_size=letter
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
# the i18n builder cannot share the environment and doctrees with the others
I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext apidoc
help:
@echo "Please use \`make <target>' where <target> is one of"
@echo " html to make standalone HTML files"
@echo " dirhtml to make HTML files named index.html in directories"
@echo " singlehtml to make a single large HTML file"
@echo " pickle to make pickle files"
@echo " json to make JSON files"
@echo " htmlhelp to make HTML files and a HTML help project"
@echo " qthelp to make HTML files and a qthelp project"
@echo " devhelp to make HTML files and a Devhelp project"
@echo " epub to make an epub"
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
@echo " latexpdf to make LaTeX files and run them through pdflatex"
@echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
@echo " text to make text files"
@echo " man to make manual pages"
@echo " texinfo to make Texinfo files"
@echo " info to make Texinfo files and run them through makeinfo"
@echo " gettext to make PO message catalogs"
@echo " changes to make an overview of all changed/added/deprecated items"
@echo " xml to make Docutils-native XML files"
@echo " pseudoxml to make pseudoxml-XML files for display purposes"
@echo " linkcheck to check all external links for integrity"
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
clean:
rm -rf $(BUILDDIR)/*
html:
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
dirhtml:
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
singlehtml:
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
@echo
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
pickle:
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
@echo
@echo "Build finished; now you can process the pickle files."
json:
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
@echo
@echo "Build finished; now you can process the JSON files."
htmlhelp:
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
@echo
@echo "Build finished; now you can run HTML Help Workshop with the" \
".hhp project file in $(BUILDDIR)/htmlhelp."
qthelp:
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
@echo
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/mit_lti_flask_sample.qhcp"
@echo "To view the help file:"
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/mit_lti_flask_sample.qhc"
devhelp:
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
@echo
@echo "Build finished."
@echo "To view the help file:"
@echo "# mkdir -p $$HOME/.local/share/devhelp/mit_lti_flask_sample"
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/mit_lti_flask_sample"
@echo "# devhelp"
epub:
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
@echo
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
latex:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
@echo "Run \`make' in that directory to run these through (pdf)latex" \
"(use \`make latexpdf' here to do that automatically)."
latexpdf:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo "Running LaTeX files through pdflatex..."
$(MAKE) -C $(BUILDDIR)/latex all-pdf
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
latexpdfja:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo "Running LaTeX files through platex and dvipdfmx..."
$(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
text:
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
@echo
@echo "Build finished. The text files are in $(BUILDDIR)/text."
man:
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
@echo
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
texinfo:
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
@echo
@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
@echo "Run \`make' in that directory to run these through makeinfo" \
"(use \`make info' here to do that automatically)."
info:
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
@echo "Running Texinfo files through makeinfo..."
make -C $(BUILDDIR)/texinfo info
@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
gettext:
$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
@echo
@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
changes:
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
@echo
@echo "The overview file is in $(BUILDDIR)/changes."
linkcheck:
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
@echo
@echo "Link check complete; look for any errors in the above output " \
"or in $(BUILDDIR)/linkcheck/output.txt."
doctest:
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
@echo "Testing of doctests in the sources finished, look at the " \
"results in $(BUILDDIR)/doctest/output.txt."
xml:
$(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
@echo
@echo "Build finished. The XML files are in $(BUILDDIR)/xml."
pseudoxml:
$(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
@echo
@echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
apidoc:
$(SPHINXAPIDOC) --separate $(SPHINXOPTS) -o $(BUILDDIR)/apidocs .
@echo
@echo "Build finished. The apidoc files are in $(BUILDDIR)/apidocs."
Modifications:
Copyright (c) 2011 Kenneth Reitz.
Original Project:
Copyright (c) 2010 by Armin Ronacher.
Some rights reserved.
Redistribution and use in source and binary forms of the theme, with or
without modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
* The names of the contributors may not be used to endorse or
promote products derived from this software without specific
prior written permission.
We kindly ask you to only use these themes in an unmodified manner just
for Flask and Flask-related products, not for unrelated projects. If you
like the visual style and want to use it for your own projects, please
consider making some larger changes to the themes (such as changing
font faces, sizes, colors or margins).
THIS THEME IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS THEME, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
krTheme Sphinx Style
====================
This repository contains sphinx styles Kenneth Reitz uses in most of
his projects. It is a derivative of Mitsuhiko's themes for Flask and Flask related
projects. To use this style in your Sphinx documentation, follow
this guide:
1. put this folder as _themes into your docs folder. Alternatively
you can also use git submodules to check out the contents there.
2. add this to your conf.py: ::
sys.path.append(os.path.abspath('_themes'))
html_theme_path = ['_themes']
html_theme = 'flask'
The following themes exist:
**kr**
the standard flask documentation theme for large projects
**kr_small**
small one-page theme. Intended to be used by very small addon libraries.
# flasky extensions. flasky pygments style based on tango style
from pygments.style import Style
from pygments.token import Keyword, Name, Comment, String, Error, \
Number, Operator, Generic, Whitespace, Punctuation, Other, Literal
class FlaskyStyle(Style):
background_color = "#f8f8f8"
default_style = ""
styles = {
# No corresponding class for the following:
#Text: "", # class: ''
Whitespace: "underline #f8f8f8", # class: 'w'
Error: "#a40000 border:#ef2929", # class: 'err'
Other: "#000000", # class 'x'
Comment: "italic #8f5902", # class: 'c'
Comment.Preproc: "noitalic", # class: 'cp'
Keyword: "bold #004461", # class: 'k'
Keyword.Constant: "bold #004461", # class: 'kc'
Keyword.Declaration: "bold #004461", # class: 'kd'
Keyword.Namespace: "bold #004461", # class: 'kn'
Keyword.Pseudo: "bold #004461", # class: 'kp'
Keyword.Reserved: "bold #004461", # class: 'kr'
Keyword.Type: "bold #004461", # class: 'kt'
Operator: "#582800", # class: 'o'
Operator.Word: "bold #004461", # class: 'ow' - like keywords
Punctuation: "bold #000000", # class: 'p'
# because special names such as Name.Class, Name.Function, etc.
# are not recognized as such later in the parsing, we choose them
# to look the same as ordinary variables.
Name: "#000000", # class: 'n'
Name.Attribute: "#c4a000", # class: 'na' - to be revised
Name.Builtin: "#004461", # class: 'nb'
Name.Builtin.Pseudo: "#3465a4", # class: 'bp'
Name.Class: "#000000", # class: 'nc' - to be revised
Name.Constant: "#000000", # class: 'no' - to be revised
Name.Decorator: "#888", # class: 'nd' - to be revised
Name.Entity: "#ce5c00", # class: 'ni'
Name.Exception: "bold #cc0000", # class: 'ne'
Name.Function: "#000000", # class: 'nf'
Name.Property: "#000000", # class: 'py'
Name.Label: "#f57900", # class: 'nl'
Name.Namespace: "#000000", # class: 'nn' - to be revised
Name.Other: "#000000", # class: 'nx'
Name.Tag: "bold #004461", # class: 'nt' - like a keyword
Name.Variable: "#000000", # class: 'nv' - to be revised
Name.Variable.Class: "#000000", # class: 'vc' - to be revised
Name.Variable.Global: "#000000", # class: 'vg' - to be revised
Name.Variable.Instance: "#000000", # class: 'vi' - to be revised
Number: "#990000", # class: 'm'
Literal: "#000000", # class: 'l'
Literal.Date: "#000000", # class: 'ld'
String: "#4e9a06", # class: 's'
String.Backtick: "#4e9a06", # class: 'sb'
String.Char: "#4e9a06", # class: 'sc'
String.Doc: "italic #8f5902", # class: 'sd' - like a comment
String.Double: "#4e9a06", # class: 's2'
String.Escape: "#4e9a06", # class: 'se'
String.Heredoc: "#4e9a06", # class: 'sh'
String.Interpol: "#4e9a06", # class: 'si'
String.Other: "#4e9a06", # class: 'sx'
String.Regex: "#4e9a06", # class: 'sr'
String.Single: "#4e9a06", # class: 's1'
String.Symbol: "#4e9a06", # class: 'ss'
Generic: "#000000", # class: 'g'
Generic.Deleted: "#a40000", # class: 'gd'
Generic.Emph: "italic #000000", # class: 'ge'
Generic.Error: "#ef2929", # class: 'gr'
Generic.Heading: "bold #000080", # class: 'gh'
Generic.Inserted: "#00A000", # class: 'gi'
Generic.Output: "#888", # class: 'go'
Generic.Prompt: "#745334", # class: 'gp'
Generic.Strong: "bold #000000", # class: 'gs'
Generic.Subheading: "bold #800080", # class: 'gu'
Generic.Traceback: "bold #a40000", # class: 'gt'
}
{%- extends "basic/layout.html" %}
{%- block extrahead %}
{{ super() }}
{% if theme_touch_icon %}
<link rel="apple-touch-icon" href="{{ pathto('_static/' ~ theme_touch_icon, 1) }}" />
{% endif %}
<meta name="viewport" content="width=device-width, initial-scale=0.9, maximum-scale=0.9">
<script type="text/javascript" src="https://gumroad.com/js/gumroad.js"></script>
{% endblock %}
{%- block relbar2 %}{% endblock %}
{%- block footer %}
<div class="footer">
&copy; Copyright {{ copyright }}.
</div>
<a href="https://github.com/mitodl/mit_lti_flask_sample" class="github">
<img style="position: absolute; top: 0; right: 0; border: 0;" src="http://s3.amazonaws.com/github/ribbons/forkme_right_darkblue_121621.png" alt="Fork me on GitHub" class="github"/>
</a>
<script type="text/javascript">
/* <![CDATA[ */
(function() {
var s = document.createElement('script'), t = document.getElementsByTagName('script')[0];
s.type = 'text/javascript';
s.async = true;
s.src = 'http://api.flattr.com/js/0.6/load.js?mode=auto';
t.parentNode.insertBefore(s, t);
})();
/* ]]> */
</script>
<script type="text/javascript">
setTimeout(function(){var a=document.createElement("script");
var b=document.getElementsByTagName("script")[0];
a.src=document.location.protocol+"//dnn506yrbagrg.cloudfront.net/pages/scripts/0013/7219.js?"+Math.floor(new Date().getTime()/3600000);
a.async=true;a.type="text/javascript";b.parentNode.insertBefore(a,b)}, 1);
</script>
<script type="text/javascript">
new HelloBar(36402,48802);
</script>
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-8742933-11']);
_gaq.push(['_setDomainName', 'none']);
_gaq.push(['_setAllowLinker', true]);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>
<script type="text/javascript">
(function() {
var t = document.createElement('script');
t.type = 'text/javascript';
t.async = true;
t.id = 'gauges-tracker';
t.setAttribute('data-site-id',
'4ddc27f6613f5d186d000007');
t.src = '//secure.gaug.es/track.js';
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(t, s);
})();
</script>
{%- endblock %}
<h3>Related Topics</h3>
<ul>
<li><a href="{{ pathto(master_doc) }}">Documentation overview</a><ul>
{%- for parent in parents %}
<li><a href="{{ parent.link|e }}">{{ parent.title }}</a><ul>
{%- endfor %}
{%- if prev %}
<li>Previous: <a href="{{ prev.link|e }}" title="{{ _('previous chapter')
}}">{{ prev.title }}</a></li>
{%- endif %}
{%- if next %}
<li>Next: <a href="{{ next.link|e }}" title="{{ _('next chapter')
}}">{{ next.title }}</a></li>
{%- endif %}
{%- for parent in parents %}
</ul></li>
{%- endfor %}
</ul></li>
</ul>
/*
* flasky.css_t
* ~~~~~~~~~~~~
*
* :copyright: Copyright 2010 by Armin Ronacher. Modifications by Kenneth Reitz.
* :license: Flask Design License, see LICENSE for details.
*/
{% set page_width = '940px' %}
{% set sidebar_width = '220px' %}
@import url("basic.css");
/* -- page layout ----------------------------------------------------------- */
body {
font-family: 'goudy old style', 'minion pro', 'bell mt', Georgia, 'Hiragino Mincho Pro';
font-size: 17px;
background-color: white;
color: #000;
margin: 0;
padding: 0;
}
div.document {
width: {{ page_width }};
margin: 30px auto 0 auto;
}
div.documentwrapper {
float: left;
width: 100%;
}
div.bodywrapper {
margin: 0 0 0 {{ sidebar_width }};
}
div.sphinxsidebar {
width: {{ sidebar_width }};
}
hr {
border: 1px solid #B1B4B6;
}
div.body {
background-color: #ffffff;
color: #3E4349;
padding: 0 30px 0 30px;
}
img.floatingflask {
padding: 0 0 10px 10px;
float: right;
}
div.footer {
width: {{ page_width }};
margin: 20px auto 30px auto;
font-size: 14px;
color: #888;
text-align: right;
}
div.footer a {
color: #888;
}
div.related {
display: none;
}
div.sphinxsidebar a {
color: #444;
text-decoration: none;
border-bottom: 1px dotted #999;
}
div.sphinxsidebar a:hover {
border-bottom: 1px solid #999;
}
div.sphinxsidebar {
font-size: 14px;
line-height: 1.5;
}
div.sphinxsidebarwrapper {
padding: 18px 10px;
}
div.sphinxsidebarwrapper p.logo {
padding: 0;
margin: -10px 0 0 -20px;
text-align: center;
}
div.sphinxsidebar h3,
div.sphinxsidebar h4 {
font-family: 'Garamond', 'Georgia', serif;
color: #444;
font-size: 24px;
font-weight: normal;
margin: 0 0 5px 0;
padding: 0;
}
div.sphinxsidebar h4 {
font-size: 20px;
}
div.sphinxsidebar h3 a {
color: #444;
}
div.sphinxsidebar p.logo a,
div.sphinxsidebar h3 a,
div.sphinxsidebar p.logo a:hover,
div.sphinxsidebar h3 a:hover {
border: none;
}
div.sphinxsidebar p {
color: #555;
margin: 10px 0;
}
div.sphinxsidebar ul {
margin: 10px 0;
padding: 0;
color: #000;
}
div.sphinxsidebar input {
border: 1px solid #ccc;
font-family: 'Georgia', serif;
font-size: 1em;
}
/* -- body styles ----------------------------------------------------------- */
a {
color: #004B6B;
text-decoration: underline;
}
a:hover {
color: #6D4100;
text-decoration: underline;
}
div.body h1,
div.body h2,
div.body h3,
div.body h4,
div.body h5,
div.body h6 {
font-family: 'Garamond', 'Georgia', serif;
font-weight: normal;
margin: 30px 0px 10px 0px;
padding: 0;
}
div.body h1 { margin-top: 0; padding-top: 0; font-size: 240%; }
div.body h2 { font-size: 180%; }
div.body h3 { font-size: 150%; }
div.body h4 { font-size: 130%; }
div.body h5 { font-size: 100%; }
div.body h6 { font-size: 100%; }
a.headerlink {
color: #ddd;
padding: 0 4px;
text-decoration: none;
}
a.headerlink:hover {
color: #444;
background: #eaeaea;
}
div.body p, div.body dd, div.body li {
line-height: 1.4em;
}
div.admonition {
background: #fafafa;
margin: 20px -30px;
padding: 10px 30px;
border-top: 1px solid #ccc;
border-bottom: 1px solid #ccc;
}
div.admonition tt.xref, div.admonition a tt {
border-bottom: 1px solid #fafafa;
}
dd div.admonition {
margin-left: -60px;
padding-left: 60px;
}
div.admonition p.admonition-title {
font-family: 'Garamond', 'Georgia', serif;
font-weight: normal;
font-size: 24px;
margin: 0 0 10px 0;
padding: 0;
line-height: 1;
}
div.admonition p.last {
margin-bottom: 0;
}
div.highlight {
background-color: white;
}
dt:target, .highlight {
background: #FAF3E8;
}
div.note {
background-color: #eee;
border: 1px solid #ccc;
}
div.seealso {
background-color: #ffc;
border: 1px solid #ff6;
}
div.topic {
background-color: #eee;
}
p.admonition-title {
display: inline;
}
p.admonition-title:after {
content: ":";
}
pre, tt {
font-family: 'Consolas', 'Menlo', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
font-size: 0.9em;
}
img.screenshot {
}
tt.descname, tt.descclassname {
font-size: 0.95em;
}
tt.descname {
padding-right: 0.08em;
}
img.screenshot {
-moz-box-shadow: 2px 2px 4px #eee;
-webkit-box-shadow: 2px 2px 4px #eee;
box-shadow: 2px 2px 4px #eee;
}
table.docutils {
border: 1px solid #888;
-moz-box-shadow: 2px 2px 4px #eee;
-webkit-box-shadow: 2px 2px 4px #eee;
box-shadow: 2px 2px 4px #eee;
}
table.docutils td, table.docutils th {
border: 1px solid #888;
padding: 0.25em 0.7em;
}
table.field-list, table.footnote {
border: none;
-moz-box-shadow: none;
-webkit-box-shadow: none;
box-shadow: none;
}
table.footnote {
margin: 15px 0;
width: 100%;
border: 1px solid #eee;
background: #fdfdfd;
font-size: 0.9em;
}
table.footnote + table.footnote {
margin-top: -15px;
border-top: none;
}
table.field-list th {
padding: 0 0.8em 0 0;
}
table.field-list td {
padding: 0;
}
table.footnote td.label {
width: 0px;
padding: 0.3em 0 0.3em 0.5em;
}
table.footnote td {
padding: 0.3em 0.5em;
}
dl {
margin: 0;
padding: 0;
}
dl dd {
margin-left: 30px;
}
blockquote {
margin: 0 0 0 30px;
padding: 0;
}
ul, ol {
margin: 10px 0 10px 30px;
padding: 0;
}
pre {
background: #eee;
padding: 7px 30px;
margin: 15px -30px;
line-height: 1.3em;
}
dl pre, blockquote pre, li pre {
margin-left: -60px;
padding-left: 60px;
}
dl dl pre {
margin-left: -90px;
padding-left: 90px;
}
tt {
background-color: #ecf0f3;
color: #222;
/* padding: 1px 2px; */
}
tt.xref, a tt {
background-color: #FBFBFB;
border-bottom: 1px solid white;
}
a.reference {
text-decoration: none;
border-bottom: 1px dotted #004B6B;
}
a.reference:hover {
border-bottom: 1px solid #6D4100;
}
a.footnote-reference {
text-decoration: none;
font-size: 0.7em;
vertical-align: top;
border-bottom: 1px dotted #004B6B;
}
a.footnote-reference:hover {
border-bottom: 1px solid #6D4100;
}
a:hover tt {
background: #EEE;
}
@media screen and (max-width: 870px) {
div.sphinxsidebar {
display: none;
}
div.document {
width: 100%;
}
div.documentwrapper {
margin-left: 0;
margin-top: 0;
margin-right: 0;
margin-bottom: 0;
}
div.bodywrapper {
margin-top: 0;
margin-right: 0;
margin-bottom: 0;
margin-left: 0;
}
ul {
margin-left: 0;
}
.document {
width: auto;
}
.footer {
width: auto;
}
.bodywrapper {
margin: 0;
}
.footer {
width: auto;
}
.github {
display: none;
}
}
@media screen and (max-width: 875px) {
body {
margin: 0;
padding: 20px 30px;
}
div.documentwrapper {
float: none;
background: white;
}
div.sphinxsidebar {
display: block;
float: none;
width: 102.5%;
margin: 50px -30px -20px -30px;
padding: 10px 20px;
background: #333;
color: white;
}
div.sphinxsidebar h3, div.sphinxsidebar h4, div.sphinxsidebar p,
div.sphinxsidebar h3 a {
color: white;
}
div.sphinxsidebar a {
color: #aaa;
}
div.sphinxsidebar p.logo {
display: none;
}
div.document {
width: 100%;
margin: 0;
}
div.related {
display: block;
margin: 0;
padding: 10px 0 20px 0;
}
div.related ul,
div.related ul li {
margin: 0;
padding: 0;
}
div.footer {
display: none;
}
div.bodywrapper {
margin: 0;
}
div.body {
min-height: 0;
padding: 0;
}
.rtd_doc_footer {
display: none;
}
.document {
width: auto;
}
.footer {
width: auto;
}
.footer {
width: auto;
}
.github {
display: none;
}
}
/* misc. */
.revsys-inline {
display: none!important;
}
\ No newline at end of file
[theme]
inherit = basic
stylesheet = flasky.css
pygments_style = flask_theme_support.FlaskyStyle
[options]
touch_icon =
{% extends "basic/layout.html" %}
{% block header %}
{{ super() }}
{% if pagename == 'index' %}
<div class=indexwrapper>
{% endif %}
{% endblock %}
{% block footer %}
{% if pagename == 'index' %}
</div>
{% endif %}
{% endblock %}
{# do not display relbars #}
{% block relbar1 %}{% endblock %}
{% block relbar2 %}
{% if theme_github_fork %}
<a href="http://github.com/{{ theme_github_fork }}"><img style="position: fixed; top: 0; right: 0; border: 0;"
src="http://s3.amazonaws.com/github/ribbons/forkme_right_darkblue_121621.png" alt="Fork me on GitHub" /></a>
{% endif %}
{% endblock %}
{% block sidebar1 %}{% endblock %}
{% block sidebar2 %}{% endblock %}
/*
* flasky.css_t
* ~~~~~~~~~~~~
*
* Sphinx stylesheet -- flasky theme based on nature theme.
*
* :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
@import url("basic.css");
/* -- page layout ----------------------------------------------------------- */
body {
font-family: 'Georgia', serif;
font-size: 17px;
color: #000;
background: white;
margin: 0;
padding: 0;
}
div.documentwrapper {
float: left;
width: 100%;
}
div.bodywrapper {
margin: 40px auto 0 auto;
width: 700px;
}
hr {
border: 1px solid #B1B4B6;
}
div.body {
background-color: #ffffff;
color: #3E4349;
padding: 0 30px 30px 30px;
}
img.floatingflask {
padding: 0 0 10px 10px;
float: right;
}
div.footer {
text-align: right;
color: #888;
padding: 10px;
font-size: 14px;
width: 650px;
margin: 0 auto 40px auto;
}
div.footer a {
color: #888;
text-decoration: underline;
}
div.related {
line-height: 32px;
color: #888;
}
div.related ul {
padding: 0 0 0 10px;
}
div.related a {
color: #444;
}
/* -- body styles ----------------------------------------------------------- */
a {
color: #004B6B;
text-decoration: underline;
}
a:hover {
color: #6D4100;
text-decoration: underline;
}
div.body {
padding-bottom: 40px; /* saved for footer */
}
div.body h1,
div.body h2,
div.body h3,
div.body h4,
div.body h5,
div.body h6 {
font-family: 'Garamond', 'Georgia', serif;
font-weight: normal;
margin: 30px 0px 10px 0px;
padding: 0;
}
{% if theme_index_logo %}
div.indexwrapper h1 {
text-indent: -999999px;
background: url({{ theme_index_logo }}) no-repeat center center;
height: {{ theme_index_logo_height }};
}
{% endif %}
div.body h2 { font-size: 180%; }
div.body h3 { font-size: 150%; }
div.body h4 { font-size: 130%; }
div.body h5 { font-size: 100%; }
div.body h6 { font-size: 100%; }
a.headerlink {
color: white;
padding: 0 4px;
text-decoration: none;
}
a.headerlink:hover {
color: #444;
background: #eaeaea;
}
div.body p, div.body dd, div.body li {
line-height: 1.4em;
}
div.admonition {
background: #fafafa;
margin: 20px -30px;
padding: 10px 30px;
border-top: 1px solid #ccc;
border-bottom: 1px solid #ccc;
}
div.admonition p.admonition-title {
font-family: 'Garamond', 'Georgia', serif;
font-weight: normal;
font-size: 24px;
margin: 0 0 10px 0;
padding: 0;
line-height: 1;
}
div.admonition p.last {
margin-bottom: 0;
}
div.highlight{
background-color: white;
}
dt:target, .highlight {
background: #FAF3E8;
}
div.note {
background-color: #eee;
border: 1px solid #ccc;
}
div.seealso {
background-color: #ffc;
border: 1px solid #ff6;
}
div.topic {
background-color: #eee;
}
div.warning {
background-color: #ffe4e4;
border: 1px solid #f66;
}
p.admonition-title {
display: inline;
}
p.admonition-title:after {
content: ":";
}
pre, tt {
font-family: 'Consolas', 'Menlo', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
font-size: 0.85em;
}
img.screenshot {
}
tt.descname, tt.descclassname {
font-size: 0.95em;
}
tt.descname {
padding-right: 0.08em;
}
img.screenshot {
-moz-box-shadow: 2px 2px 4px #eee;
-webkit-box-shadow: 2px 2px 4px #eee;
box-shadow: 2px 2px 4px #eee;
}
table.docutils {
border: 1px solid #888;
-moz-box-shadow: 2px 2px 4px #eee;
-webkit-box-shadow: 2px 2px 4px #eee;
box-shadow: 2px 2px 4px #eee;
}
table.docutils td, table.docutils th {
border: 1px solid #888;
padding: 0.25em 0.7em;
}
table.field-list, table.footnote {
border: none;
-moz-box-shadow: none;
-webkit-box-shadow: none;
box-shadow: none;
}
table.footnote {
margin: 15px 0;
width: 100%;
border: 1px solid #eee;
}
table.field-list th {
padding: 0 0.8em 0 0;
}
table.field-list td {
padding: 0;
}
table.footnote td {
padding: 0.5em;
}
dl {
margin: 0;
padding: 0;
}
dl dd {
margin-left: 30px;
}
pre {
padding: 0;
margin: 15px -30px;
padding: 8px;
line-height: 1.3em;
padding: 7px 30px;
background: #eee;
border-radius: 2px;
-moz-border-radius: 2px;
-webkit-border-radius: 2px;
}
dl pre {
margin-left: -60px;
padding-left: 60px;
}
tt {
background-color: #ecf0f3;
color: #222;
/* padding: 1px 2px; */
}
tt.xref, a tt {
background-color: #FBFBFB;
}
a:hover tt {
background: #EEE;
}
[theme]
inherit = basic
stylesheet = flasky.css
nosidebar = true
pygments_style = flask_theme_support.FlaskyStyle
[options]
index_logo = ''
index_logo_height = 120px
github_fork = ''
Command Line Tools
==================
There are a variety of command line tools you will require to complete this
tutorial. Most are available by default on Unix-based operating systems like
Linux or OSX. Some you must explicitly install.
OSX
---
OSX doesn't come with some of these command line tools by default. Fortunately
they are easy to install on OSX 10.9 Mavericks and newer. Open the Terminal
application, found in ``/Applications/Utilities/`` and enter:
.. code-block:: bash
make
These newer versions of OSX recognize that ``make`` is one of the command line
tools not included in the base OSX installation and offers to install all the
command line tools. This download is free.
In older versions of OSX, you can still open the Terminal application and
enter:
.. code-block:: bash
xcode-select --install
This command will do the same thing explicitly.
You can also go directly to the Apple Developer site and download the command
line tools directly. The site will require you to register, but that is free.
This is the URL of the Apple Developer Downloads site:
`https://developer.apple.com/downloads/index.action
<https://developer.apple.com/downloads/index.action>`_ Search for the latest
version of the command line tools for your OSX version.
Linux
-----
Linux systems have a package manager to install new applications and manage
existing ones. You can check your system's documentation for guidance on its
use.
For debian based systems including Ubuntu you need to run:
.. code-block:: bash
sudo apt-get install build-essential
For RedHat based distributions, including Fedora and CentOS, run:
.. code-block:: bash
sudo yum groupinstall "Development Tools"
# -*- coding: utf-8 -*-
#
# mit_lti_flask_sample documentation build configuration file, created by
# sphinx-quickstart on Wed Oct 15 13:13:02 2014.
#
# This file is execfile()d with the current directory set to its
# containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.
import sys
import os
sys.path.append(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))
from mit_lti_flask_sample import VERSION
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#sys.path.insert(0, os.path.abspath('.'))
# -- General configuration ------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
'sphinx.ext.autodoc',
'sphinx.ext.doctest',
'sphinx.ext.todo',
'sphinx.ext.coverage',
'sphinx.ext.pngmath',
]
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The suffix of source filenames.
source_suffix = '.rst'
# The encoding of source files.
#source_encoding = 'utf-8-sig'
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = u'LTI Provider for Flask'
copyright = u'2014, Massachusetts Institute of Technology'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = VERSION
# The full version, including alpha/beta/rc tags.
release = VERSION
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#language = None
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ''
# Else, today_fmt is used as the format for a strftime call.
#today_fmt = '%B %d, %Y'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = ['_themes/README.rst',]
# List of directories, relative to source directory, that shouldn't be searched
# for source files.
exclude_trees = ['_build']
# The reST default role (used for this markup: `text`) to use for all
# documents.
#default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
#add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
#add_module_names = True
# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
#show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []
# If true, keep warnings as "system message" paragraphs in the built documents.
#keep_warnings = False
# -- Options for HTML output ----------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
html_theme = 'kr'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#html_theme_options = {}
# Add any paths that contain custom themes here, relative to this directory.
sys.path.append(os.path.abspath('_themes'))
html_theme_path = ['_themes']
# The name for this set of Sphinx documents. If None, it defaults to
# "<project> v<release> documentation".
#html_title = None
# A shorter title for the navigation bar. Default is the same as html_title.
#html_short_title = None
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
#html_logo = None
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
#html_favicon = None
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
# Add any extra paths that contain custom files (such as robots.txt or
# .htaccess) here, relative to this directory. These files are copied
# directly to the root of the documentation.
#html_extra_path = []
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
#html_last_updated_fmt = '%b %d, %Y'
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
#html_sidebars = {}
# Additional templates that should be rendered to pages, maps page names to
# template names.
#html_additional_pages = {}
# If false, no module index is generated.
#html_domain_indices = True
# If false, no index is generated.
#html_use_index = True
# If true, the index is split into individual pages for each letter.
#html_split_index = False
# If true, links to the reST sources are added to the pages.
#html_show_sourcelink = True
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
#html_show_sphinx = True
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
#html_show_copyright = True
# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it. The value of this option must be the
# base URL from which the finished HTML is served.
#html_use_opensearch = ''
# This is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = None
# Output file base name for HTML help builder.
htmlhelp_basename = 'mit_lti_flask_sampledoc'
# -- Options for LaTeX output ---------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#'preamble': '',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
('index', 'mit_lti_flask_sample.tex', u'mit\\_lti\\_flask\\_sample Documentation',
u'Massachusetts Institute of Technology', 'manual'),
]
# The name of an image file (relative to this directory) to place at the top of
# the title page.
#latex_logo = None
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
#latex_use_parts = False
# If true, show page references after internal links.
#latex_show_pagerefs = False
# If true, show URL addresses after external links.
#latex_show_urls = False
# Documents to append as an appendix to all manuals.
#latex_appendices = []
# If false, no module index is generated.
#latex_domain_indices = True
# -- Options for manual page output ---------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
('index', 'mit_lti_flask_sample', u'mit_lti_flask_sample Documentation',
[u'Massachusetts Institute of Technology'], 1)
]
# If true, show URL addresses after external links.
#man_show_urls = False
# -- Options for Texinfo output -------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
('index', 'mit_lti_flask_sample', u'mit_lti_flask_sample Documentation',
u'Massachusetts Institute of Technology', 'mit_lti_flask_sample', 'One line description of project.',
'Miscellaneous'),
]
# Documents to append as an appendix to all manuals.
#texinfo_appendices = []
# If false, no module index is generated.
#texinfo_domain_indices = True
# How to display URL addresses: 'footnote', 'no', or 'inline'.
#texinfo_show_urls = 'footnote'
# If true, do not generate a @detailmenu in the "Top" node's menu.
#texinfo_no_detailmenu = False
# exclude_patterns are a list of glob-style patterns that should be excluded
# when looking for source files. They are matched against the source file names
# relative to the source directory, using slashes as directory separators on
# all platforms.
exclude_patterns=['_themes/README.rst',]
# display todo notes in the documentation
todo_include_todos=True
Deploy to Heroku
================
|Deploy|
.. |Deploy| image:: https://www.herokucdn.com/deploy/button.png
:target: https://heroku.com/deploy
An LTI provider must run on a server accessible by your LTI consumer. This
documentation assumes your LTI consumer is edX, but the principle is the same
for other LTI consumers.
Heroku is a popular hosting service that offers free deployment for low volume
applications and a sliding pricing model for higher demand applications. For the
purpose of documentation we will use Heroku as our deployment target though
you may deploy to any host or hosting service you choose.
The following steps have these prerequisites:
* you have cloned the ``mit_lti_flask_sample`` to your development machine
* you can open a terminal window and navigate to the cloned sample code
* you have installed the ``git`` command line tool, or some other git client.
.. note::
The following steps presume that you have a collection of command line
utilities installed on your machine. These utilities are installed by
default on Linux machines, and freely available for OSX. This link will
guide you to resources for your operation system:
:doc:`cmd_line_tools`
Heroku Deployment
-----------------
The documentation that Heroku provides for preparing your machine and installing
required software is excellent. Start there by getting a Heroku account and
following the Python track of their tutorial.
1. Sign up for a free account by navigating to
`https://www.heroku.com <https://www.heroku.com>`_. If you already have an
account, navigate to
`https://devcenter.heroku.com/articles/getting-started-with-python#introduction
<https://devcenter.heroku.com/articles/getting-started-with-python#introduction>`_
to proceed with the Heroku tutorial.
#. Once you have an account, the Heroku site will guide you to install the Heroku
Toolbelt and upload your SSH keypair. Your SSH keypair authenticates you to
Heroku. If you don't have a SSH keypair, Heroku provides a link on that page
to help you create one.
#. The next step of the Heroku tutorial, "Prepare the app", presumes you don't
have an app to deploy, but you do. Before continuing with the Heroku tutorial,
let's first confirm that your app will run in the Heroku environment. Do
this by using an application installed as part of the Heroku Toolbelt.
#. In your terminal window, navigate to the file system directory that contains
the sample application you cloned earlier. If you haven't cloned the sample
app to a local directory, see this guide :doc:`tutorial`.)
#. Heroku provides a development server called ``foreman`` as part of its
"Heroku Toolbelt". (You should have installed the "Toolbelt" in the
'Introduction' step of the Heroku 'getting started' process.) Foreman
allows you to simulate the Heroku runtime environment locally; very handy
to help you verify that your app will run once deployed. You can run your
app with this terminal command:
.. code-block:: bash
foreman start
6. Once your app starts, navigate to
``http://localhost:5000/is_up`` to confirm that the app is working. The
page will display the text "I'm up" if the app is working properly. To
terminate the app, type ``Cntl-C`` in the terminal.
#. Now that you know that your app will run in the Heroku environment, your
next step is to deploy your app to Heroku. Proceed to the "Deploy the app"
step in the Heroku tutorial.Use the ``create`` command to deploy your app:
.. code-block:: bash
heroku create
Creating salty-tundra-1591... done, stack is cedar-14
https://salty-tundra-1591.herokuapp.com/ | git@heroku.com:salty-tundra-1591.git
The ``create`` command returns useful information. It indicates
* success or failure,
* the Heroku platform stack that your app runs in,
* the URL for your deployed app on Heroku
(In this case, https://salty-tundra-1591.herokuapp.com/)
* the Git URL for your Git repository on Heroku
(In this case, git@heroku.com:salty-tundra-1591.git)
You can now use the URL of your deployed app on Heroku to test the sample
against your edX course.
Additional Heroku Configuration
-------------------------------
We recommend not storing secrets inside the git repository such as the
OAuth keys needed for LTI security. As a result, we have instrumented
the ``config.py`` file to have an example of using environment
variables to store these secrets.
For example:
.. code-block:: python
# secret key for authentication
SECRET_KEY = os.environ.get("FLASK_SECRET_KEY", "you-will-never-guess")
CONSUMER_KEY_PEM_FILE = os.path.abspath('consumer_key.pem')
with open(CONSUMER_KEY_PEM_FILE, 'w') as wfile:
wfile.write(os.environ.get('CONSUMER_KEY_CERT', ''))
PYLTI_CONFIG = {
"consumers": {
"__consumer_key__": {
"secret": os.environ.get("CONSUMER_KEY_SECRET", "__lti_secret__"),
"cert": CONSUMER_KEY_PEM_FILE
}
}
}
Now it is attempting to get the ``FLASK_SECRET_KEY``, the
``CONSUMER_KEY_CERT`` and ``CONSUMER_KEY_SECRET`` environment
variable values for the actual secrets. To do this in Heroku you
can set these variables with the ``heroku config`` commands. To set
the flask secret to ``pink_unicorns`` and ``__consumer_key__`` secret
to ``horn_of_plenty`` you would run:
.. code-block:: bash
heroku config:set FLASK_SECRET_KEY=pink_unicorn CONSUMER_KEY_SECRET=horn_of_plenty
To check your configuration, you can run ``heroku config`` by itself,
and it will show what environment variables are set for your
application.
To replicate the secure configuration locally using ``foreman`` you can create a file in the root of the application at ``.env`` that contains K=V values for configuration. i.e.
.. code-block:: bash
FLASK_SECRET_KEY=pink_unicorn
CONSUMER_KEY_SECRET=horn_of_plenty
.. note::
Environment variables can be absolutely huge, so there is no problem
storing full client SSL certificates in the
``CONSUMER_KEY_PEM_FILE`` if your application requires client
certificates in addition to the OAuth scheme. ``config.py`` above,
for example, reads the environment variable the SSL certificate and
key and writes it out to a file for use by ``httplib`` during
execution on Heroku.
.. note::
Developers whose LTI app will be consumed by MITx will need an application
certificate issued by MIT IS&T to be able to send grades to edX. You can
request an application certificate by following the instructions at this
support page
`https://wikis.mit.edu/confluence/display/devtools/How+to+acquire+and+verify+a+x509+Application+Certificate
<https://wikis.mit.edu/confluence/display/devtools/How+to+acquire+and+verify+a+x509+Application+Certificate>`_
Files added for Heroku Support
------------------------------
The items described below are in the sample solely to support Heroku deployment.
If you don't deploy to Heroku, you can ignore or remove them.
``Procfile``
^^^^^^^^^^^^
Heroku requires a text file named ``Procfile`` to reside in the application's
root directory. Heroku documents the contents of the Procfile here:
`https://devcenter.heroku.com/articles/procfile
<https://devcenter.heroku.com/articles/procfile>`_
``runtime.txt``
^^^^^^^^^^^^^^^
Heroku uses the contents of ``runtime.txt`` to pin a specific version of the
Python runtime to a specific version. The app has been tested with the Python
runtime version found in the file.
.. include:: ../README.rst
Contents
--------
.. toctree::
:maxdepth: 2
tutorial
deploy_to_heroku
cmd_line_tools
setup_edX_fullstack
license
Indices and tables
------------------
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`
License: BSD (FreeBSD version)
==============================
The ``mit_lti_flask_sample`` codebase is licensed under the BSD (FreeBSD)
license.
.. topic:: The BSD 2-Clause License (also known as the FreeBSD license) - Version: January 9, 2008
Copyright (c) 2014, Massachusetts Institute of Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
#. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
@ECHO OFF
REM Command file for Sphinx documentation
if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
set BUILDDIR=_build
set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
set I18NSPHINXOPTS=%SPHINXOPTS% .
if NOT "%PAPER%" == "" (
set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
)
if "%1" == "" goto help
if "%1" == "help" (
:help
echo.Please use `make ^<target^>` where ^<target^> is one of
echo. html to make standalone HTML files
echo. dirhtml to make HTML files named index.html in directories
echo. singlehtml to make a single large HTML file
echo. pickle to make pickle files
echo. json to make JSON files
echo. htmlhelp to make HTML files and a HTML help project
echo. qthelp to make HTML files and a qthelp project
echo. devhelp to make HTML files and a Devhelp project
echo. epub to make an epub
echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
echo. text to make text files
echo. man to make manual pages
echo. texinfo to make Texinfo files
echo. gettext to make PO message catalogs
echo. changes to make an overview over all changed/added/deprecated items
echo. xml to make Docutils-native XML files
echo. pseudoxml to make pseudoxml-XML files for display purposes
echo. linkcheck to check all external links for integrity
echo. doctest to run all doctests embedded in the documentation if enabled
goto end
)
if "%1" == "clean" (
for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
del /q /s %BUILDDIR%\*
goto end
)
%SPHINXBUILD% 2> nul
if errorlevel 9009 (
echo.
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
echo.installed, then set the SPHINXBUILD environment variable to point
echo.to the full path of the 'sphinx-build' executable. Alternatively you
echo.may add the Sphinx directory to PATH.
echo.
echo.If you don't have Sphinx installed, grab it from
echo.http://sphinx-doc.org/
exit /b 1
)
if "%1" == "html" (
%SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The HTML pages are in %BUILDDIR%/html.
goto end
)
if "%1" == "dirhtml" (
%SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
goto end
)
if "%1" == "singlehtml" (
%SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
goto end
)
if "%1" == "pickle" (
%SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
if errorlevel 1 exit /b 1
echo.
echo.Build finished; now you can process the pickle files.
goto end
)
if "%1" == "json" (
%SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
if errorlevel 1 exit /b 1
echo.
echo.Build finished; now you can process the JSON files.
goto end
)
if "%1" == "htmlhelp" (
%SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
if errorlevel 1 exit /b 1
echo.
echo.Build finished; now you can run HTML Help Workshop with the ^
.hhp project file in %BUILDDIR%/htmlhelp.
goto end
)
if "%1" == "qthelp" (
%SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
if errorlevel 1 exit /b 1
echo.
echo.Build finished; now you can run "qcollectiongenerator" with the ^
.qhcp project file in %BUILDDIR%/qthelp, like this:
echo.^> qcollectiongenerator %BUILDDIR%\qthelp\mit_lti_flask_sample.qhcp
echo.To view the help file:
echo.^> assistant -collectionFile %BUILDDIR%\qthelp\mit_lti_flask_sample.ghc
goto end
)
if "%1" == "devhelp" (
%SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
if errorlevel 1 exit /b 1
echo.
echo.Build finished.
goto end
)
if "%1" == "epub" (
%SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The epub file is in %BUILDDIR%/epub.
goto end
)
if "%1" == "latex" (
%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
if errorlevel 1 exit /b 1
echo.
echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
goto end
)
if "%1" == "latexpdf" (
%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
cd %BUILDDIR%/latex
make all-pdf
cd %BUILDDIR%/..
echo.
echo.Build finished; the PDF files are in %BUILDDIR%/latex.
goto end
)
if "%1" == "latexpdfja" (
%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
cd %BUILDDIR%/latex
make all-pdf-ja
cd %BUILDDIR%/..
echo.
echo.Build finished; the PDF files are in %BUILDDIR%/latex.
goto end
)
if "%1" == "text" (
%SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The text files are in %BUILDDIR%/text.
goto end
)
if "%1" == "man" (
%SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The manual pages are in %BUILDDIR%/man.
goto end
)
if "%1" == "texinfo" (
%SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
goto end
)
if "%1" == "gettext" (
%SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
goto end
)
if "%1" == "changes" (
%SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
if errorlevel 1 exit /b 1
echo.
echo.The overview file is in %BUILDDIR%/changes.
goto end
)
if "%1" == "linkcheck" (
%SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
if errorlevel 1 exit /b 1
echo.
echo.Link check complete; look for any errors in the above output ^
or in %BUILDDIR%/linkcheck/output.txt.
goto end
)
if "%1" == "doctest" (
%SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
if errorlevel 1 exit /b 1
echo.
echo.Testing of doctests in the sources finished, look at the ^
results in %BUILDDIR%/doctest/output.txt.
goto end
)
if "%1" == "xml" (
%SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The XML files are in %BUILDDIR%/xml.
goto end
)
if "%1" == "pseudoxml" (
%SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml.
goto end
)
:end
Tutorial: Setup edX
===================
The intent of this tutorial is to show how to:
* Create a virtual environment to run an edX server locally
* Download edX and run an
* Run a sample course in edX
Reference: `https://github.com/edx/configuration/wiki/edX-Production-Stack <https://github.com/edx/configuration/wiki/edX-Production-Stack>`_
Create a virtual environment and run an edX server in it
********************************************************
Download and install Vagrant and VirtualBox from:
http://www.vagrantup.com/downloads
https://www.virtualbox.org/wiki/Downloads
If you previously installed Vagrant and VirtualBox and you have
no reason to preserve existing Vagrant/VirtualBox instances:
Run VirtualBox and remove all vagrant instances.
Remove unused VMs from VirtualBox VMs.
Reboot your machine to insure that you are off to a clean start.
To ``INSTALL`` the edX production stack in a Terminal Window
(the edX production installation takes a several minutes)::
$ mkdir fullstack
$ cd fullstack
$ curl -L https://raw.githubusercontent.com/edx/configuration/master/vagrant/release/fullstack/Vagrantfile > Vagrantfile
$ vagrant plugin install vagrant-hostsupdater
The ``ACTIVATE`` instructions will complete the installation and start edX VM.
At this point you can leave the Terminal Window and the edX VM will
continue to be active.
To ``ACTIVATE`` the edX server on an existing VM::
$ vagrant status (expect "not created" or "poweroff mode")
$ vagrant halt (if the status is not "poweroff mode")
$ vagrant up (first time after install - password is for writing files to your machine)
``password:`` is for the administrator of your machine
$ vagrant ssh
$ sudo rm /edx/var/mongo/mongodb/mongod.lock
$ sudo /etc/init.d/mongodb restart
To ``DEACTIVATE`` a running edX server::
$ vagrant halt
Run a sample course in edX
**************************
Once the VM is activated, you can access the LMS and Studio at these URLS:
LMS: http://192.168.33.10/
sign in as: ``staff@example.com`` password: ``edx``
notice the edX Demo Course
Studio: http://192.168.33.10:18010/
sign in as: ``staff@example.com`` password: ``edx``
import os
from flask import Flask
from flask import render_template
from flask_wtf import Form
from wtforms import IntegerField, BooleanField, TextField
from random import randint
from pylti.flask import lti
VERSION = '0.0.1'
app = Flask(__name__)
app.config.from_object('config')
class AddForm(Form):
""" Add data from Form
:param Form:
"""
p1 = IntegerField('p1')
p2 = IntegerField('p2')
result = IntegerField('result')
correct = IntegerField('correct')
# correct = BooleanField('correct')
return_url = TextField('')
def error(exception=None):
""" render error page
:param exception: optional exception
:return: the error.html template rendered
"""
return render_template('error.html')
@app.route('/is_up', methods=['GET'])
def hello_world(lti=lti):
""" Indicate the app is working. Provided for debugging purposes.
:param lti: the `lti` object from `pylti`
:return: simple page that indicates the request was processed by the lti
provider
"""
return render_template('up.html', lti=lti)
@app.route('/', methods=['GET', 'POST'])
@app.route('/index', methods=['GET'])
@app.route('/lti/', methods=['GET', 'POST'])
@lti(request='initial', error=error, app=app)
def index(lti=lti):
""" initial access page to the lti provider. This page provides
authorization for the user.
:param lti: the `lti` object from `pylti`
:return: index page for lti provider
"""
return render_template('index.html', lti=lti)
@app.route('/index_staff', methods=['GET', 'POST'])
@lti(request='session', error=error, role='staff', app=app)
def index_staff(lti=lti):
""" render the contents of the staff.html template
:param lti: the `lti` object from `pylti`
:return: the staff.html template rendered
"""
return render_template('staff.html', lti=lti)
@app.route('/add', methods=['GET'])
@lti(request='session', error=error, app=app)
def add_form(lti=lti):
""" initial access page for lti consumer
:param lti: the `lti` object from `pylti`
:return: index page for lti provider
"""
form = AddForm()
form.p1.data = randint(1, 9)
form.p2.data = randint(1, 9)
return render_template('add.html', form=form)
@app.route('/grade', methods=['POST'])
@lti(request='session', error=error, app=app)
def grade(lti=lti):
""" post grade
:param lti: the `lti` object from `pylti`
:return: grade rendered by grade.html template
"""
form = AddForm()
correct = ((form.p1.data + form.p2.data) == form.result.data)
form.correct.data = correct
# form.return_url = lti.session['launch_presentation_return_url'] + ('&lti_msg=Good%20job' if correct else '&lti_errormsg=%20Sorry')
form.return_url = lti.session['launch_presentation_return_url']
lti.post_grade(1 if correct else 0)
return render_template('grade.html', form=form)
def set_debugging():
""" enable debug logging
"""
import logging
import sys
root = logging.getLogger()
root.setLevel(logging.DEBUG)
ch = logging.StreamHandler(sys.stdout)
ch.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(name)s - %(message)s')
ch.setFormatter(formatter)
root.addHandler(ch)
set_debugging()
if __name__ == '__main__':
"""
For if you want to run the flask development server
directly
"""
port = int(os.environ.get("FLASK_LTI_PORT", 5000))
host = os.environ.get("FLASK_LTI_HOST", "localhost")
app.run(debug=True, host=host, port=port)
werkzeug - * Running on http://0.0.0.0:5000/
werkzeug - * Restarting with reloader
pylti.common - verify request=initial
pylti.flask - {'oauth_version': '1.0', 'oauth_nonce': '1635b0cfcee2e9d5a3cb8087ec450f06', 'oauth_timestamp': '1597264480', 'oauth_consumer_key': '__consumer_key__', 'user_id': '219', 'lis_person_sourcedid': 'B6AFNQ', 'roles': 'Instructor', 'context_id': '4598', 'context_label': '_VIKOL_PROG2TST', 'context_title': 'VIK online Programozás alapjai 2.', 'resource_link_title': 'LTI teszt (Flask)', 'resource_link_description': '', 'resource_link_id': '1', 'context_type': 'CourseSection', 'lis_course_section_sourcedid': '_VIKOL_PROG2TST', 'lis_result_sourcedid': '{"data":{"instanceid":"1","userid":"219","typeid":null,"launchid":938409188},"hash":"024e2c476b7592693465aaa8582305dac2ec7ad375c2f6caed25804933a20f70"}', 'lis_outcome_service_url': 'https://edu.vik.bme.hu/mod/lti/service.php', 'lis_person_name_given': 'Imre', 'lis_person_name_family': 'Szeberényi', 'lis_person_name_full': 'Imre Szeberényi', 'ext_user_username': 'b6afnq', 'lis_person_contact_email_primary': 'szebi@iit.bme.hu', 'launch_presentation_locale': 'en', 'ext_lms': 'moodle-2', 'tool_consumer_info_product_family_code': 'moodle', 'tool_consumer_info_version': '2018120305.06', 'oauth_callback': 'about:blank', 'lti_version': 'LTI-1p0', 'lti_message_type': 'basic-lti-launch-request', 'tool_consumer_instance_guid': 'edu.vik.bme.hu', 'tool_consumer_instance_name': 'BME Villamosmérnöki és Informatikai Kar', 'tool_consumer_instance_description': 'Budapesti Műszaki és Gazdaságtudományi Egyetem - Villamosmérnöki és Informatikai Kar', 'launch_presentation_document_target': 'window', 'launch_presentation_return_url': 'https://edu.vik.bme.hu/mod/lti/return.php?course=4598&launch_container=4&instanceid=1&sesskey=MPBwjCEzcw', 'oauth_signature_method': 'HMAC-SHA1', 'oauth_signature': 'rHwSjVOl2Ig5s1MMMWg2GuFwzlQ='}
pylti.flask - verify_request?
pylti.common - consumers {'__consumer_key__': {'secret': '__lti_secret__'}}
pylti.common - url http://152.66.84.28:5000/
pylti.common - method POST
pylti.common - headers Content-Type: application/x-www-form-urlencoded
Content-Length: 1735
Host: 152.66.84.28:5000
User-Agent: Mozilla/5.0 (Windows NT 6.3; Win64; x64; rv:79.0) Gecko/20100101 Firefox/79.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: hu,en;q=0.8,hu-HU;q=0.5,en-US;q=0.3
Accept-Encoding: gzip, deflate
Origin: null
Dnt: 1
Connection: keep-alive
Cookie: session=.eJyNU11v0zAU_StTHiaQRmvns5lUAUMMTQw2WMVTJcuxb4hp4kT-6Gin_neuk2qoAgne4nPPvT7nXOcpEr128NMxJaPLKM3KRXTxjLW8ghZh9u3m490tu_969yFePawCw5qauX4DGsu0rMuiLrO6SrmsZMJzCSSPc5HHMalzKmhFMxHH2Ndyr0XDBgMWtONO9ZoZcN5o5k24qnFusJfzOUg_26rNrOpg1vh518t569R84s6GZngtem8sLIPk8-PYoJsrDWaZnittHdcClFzScwvWbmC3_HR_9fjj3fu9eAxalGW9d6LvgFkwWyXg_0QcyUHFccwAxqKT8X7hGHRctWhSddzscJ7dQ6XeKOWOk067NEcBNe9UG7gPyAWz9oRAqXfqb1TfBpE3nYGzf7O_q-24pEA_LVsMUIAcF3-Vv73-_OVYx-X41p3Un9aR5I6vo0v8-h0tntcRXUcX68hjKkcgpuUIud0wcTQqxvO0pRGhSZIRmqVJcsBCw20zdqaC0IQspACaJZLzhZBxQjJeS6jiQmaLopJxXaAqwgtZ5DWnVcKpSCHNsoqivkPw4BTj3jX4wpTgDtCAMx6mwhbN46tDT7erm1d0INjQB3bYnvUdGIYvJTz6E4CxZ57u0TsSBE0JT6RckCytqjRNSSnLvChjmSyKIs2wAZMcQ2St0pvpF6N_wE65FiY9Zw7s3p29uG653bwMzL4FG7aHkRsvXG8QDFFPwzDp6PALO_BGyA.EhXjQA.ScKfo_76dCdgAA-0F0JDJ125B24
Upgrade-Insecure-Requests: 1
pylti.common - params {'oauth_version': '1.0', 'oauth_nonce': '1635b0cfcee2e9d5a3cb8087ec450f06', 'oauth_timestamp': '1597264480', 'oauth_consumer_key': '__consumer_key__', 'user_id': '219', 'lis_person_sourcedid': 'B6AFNQ', 'roles': 'Instructor', 'context_id': '4598', 'context_label': '_VIKOL_PROG2TST', 'context_title': 'VIK online Programozás alapjai 2.', 'resource_link_title': 'LTI teszt (Flask)', 'resource_link_description': '', 'resource_link_id': '1', 'context_type': 'CourseSection', 'lis_course_section_sourcedid': '_VIKOL_PROG2TST', 'lis_result_sourcedid': '{"data":{"instanceid":"1","userid":"219","typeid":null,"launchid":938409188},"hash":"024e2c476b7592693465aaa8582305dac2ec7ad375c2f6caed25804933a20f70"}', 'lis_outcome_service_url': 'https://edu.vik.bme.hu/mod/lti/service.php', 'lis_person_name_given': 'Imre', 'lis_person_name_family': 'Szeberényi', 'lis_person_name_full': 'Imre Szeberényi', 'ext_user_username': 'b6afnq', 'lis_person_contact_email_primary': 'szebi@iit.bme.hu', 'launch_presentation_locale': 'en', 'ext_lms': 'moodle-2', 'tool_consumer_info_product_family_code': 'moodle', 'tool_consumer_info_version': '2018120305.06', 'oauth_callback': 'about:blank', 'lti_version': 'LTI-1p0', 'lti_message_type': 'basic-lti-launch-request', 'tool_consumer_instance_guid': 'edu.vik.bme.hu', 'tool_consumer_instance_name': 'BME Villamosmérnöki és Informatikai Kar', 'tool_consumer_instance_description': 'Budapesti Műszaki és Gazdaságtudományi Egyetem - Villamosmérnöki és Informatikai Kar', 'launch_presentation_document_target': 'window', 'launch_presentation_return_url': 'https://edu.vik.bme.hu/mod/lti/return.php?course=4598&launch_container=4&instanceid=1&sesskey=MPBwjCEzcw', 'oauth_signature_method': 'HMAC-SHA1', 'oauth_signature': 'rHwSjVOl2Ig5s1MMMWg2GuFwzlQ='}
pylti.flask - verify_request success
pylti.flask - params oauth_consumer_key=__consumer_key__
pylti.flask - params launch_presentation_return_url=https://edu.vik.bme.hu/mod/lti/return.php?course=4598&launch_container=4&instanceid=1&sesskey=MPBwjCEzcw
pylti.flask - params user_id=219
pylti.flask - params oauth_nonce=1635b0cfcee2e9d5a3cb8087ec450f06
pylti.flask - params context_label=_VIKOL_PROG2TST
pylti.flask - params context_id=4598
pylti.flask - params resource_link_title=LTI teszt (Flask)
pylti.flask - params resource_link_id=1
pylti.flask - params lis_person_contact_email_primary=szebi@iit.bme.hu
pylti.flask - params lis_person_name_full=Imre Szeberényi
pylti.flask - params lis_person_name_family=Szeberényi
pylti.flask - params lis_person_name_given=Imre
pylti.flask - params lis_result_sourcedid={"data":{"instanceid":"1","userid":"219","typeid":null,"launchid":938409188},"hash":"024e2c476b7592693465aaa8582305dac2ec7ad375c2f6caed25804933a20f70"}
pylti.flask - params lis_person_sourcedid=B6AFNQ
pylti.flask - params lti_version=LTI-1p0
pylti.flask - params roles=Instructor
pylti.flask - params lis_outcome_service_url=https://edu.vik.bme.hu/mod/lti/service.php
pylti.common - check_role lti_role=Instructor decorator_role=any
werkzeug - 37.76.115.28 - - [12/Aug/2020 22:36:20] "POST / HTTP/1.1" 200 -
pylti.common - verify request=session
pylti.common - check_role lti_role=Instructor decorator_role=any
werkzeug - 37.76.115.28 - - [12/Aug/2020 22:36:21] "GET /add HTTP/1.1" 200 -
pylti.common - verify request=session
pylti.common - check_role lti_role=Instructor decorator_role=any
pylti.common - XML Response:
<?xml version='1.0' encoding='utf-8'?>
<imsx_POXEnvelopeRequest xmlns="http://www.imsglobal.org/services/ltiv1p1/xsd/imsoms_v1p0"><imsx_POXHeader><imsx_POXRequestHeaderInfo><imsx_version>V1.0</imsx_version><imsx_messageIdentifier>edX_fix</imsx_messageIdentifier></imsx_POXRequestHeaderInfo></imsx_POXHeader><imsx_POXBody><replaceResultRequest><resultRecord><sourcedGUID><sourcedId>{"data":{"instanceid":"1","userid":"219","typeid":null,"launchid":938409188},"hash":"024e2c476b7592693465aaa8582305dac2ec7ad375c2f6caed25804933a20f70"}</sourcedId></sourcedGUID><result><resultScore><language>en</language><textString>0.0</textString></resultScore></result></resultRecord></replaceResultRequest></imsx_POXBody></imsx_POXEnvelopeRequest>
pylti.common - headers
pylti.common - {'Content-Type': 'application/xml', 'Authorization': 'OAuth realm="https://edu.vik.bme.hu", oauth_consumer_key="__consumer_key__", oauth_timestamp="1597264583", oauth_nonce="98163414", oauth_version="1.0", oauth_body_hash="MHIUXrL7%2ByaCBrKX7RRiRge1SLE%3D", oauth_signature_method="HMAC-SHA1", oauth_signature="TaK9oNiJWcZWEpA%2B47N5uGJ5YFc%3D"'}
pylti.common - key __consumer_key__
pylti.common - secret __lti_secret__
pylti.common - url https://edu.vik.bme.hu/mod/lti/service.php
pylti.common - response {'date': 'Wed, 12 Aug 2020 20:35:03 GMT', 'server': 'Apache', 'vary': 'Accept-Encoding', 'content-length': '705', 'content-type': 'text/html; charset=utf-8', 'status': '200', '-content-encoding': 'gzip'}
pylti.common - content b'<?xml version="1.0" encoding="UTF-8"?>\n<imsx_POXEnvelopeResponse xmlns="http://www.imsglobal.org/services/ltiv1p1/xsd/imsoms_v1p0"><imsx_POXHeader><imsx_POXResponseHeaderInfo><imsx_version>V1.0</imsx_version><imsx_messageIdentifier>223928530</imsx_messageIdentifier><imsx_statusInfo><imsx_codeMajor>success</imsx_codeMajor><imsx_severity>status</imsx_severity><imsx_description>Grade replace response</imsx_description><imsx_messageRefIdentifier>edX_fix</imsx_messageRefIdentifier><imsx_operationRefIdentifier>replaceResultRequest</imsx_operationRefIdentifier></imsx_statusInfo></imsx_POXResponseHeaderInfo></imsx_POXHeader><imsx_POXBody><replaceResultResponse/></imsx_POXBody></imsx_POXEnvelopeResponse>\n'
pylti.common - is success True
werkzeug - 37.76.115.28 - - [12/Aug/2020 22:36:23] "POST /grade HTTP/1.1" 200 -
Flask==0.12.3
Flask-WTF==0.10.2
Jinja2==2.7.3
MarkupSafe==0.23
Pygments==1.6
WTForms==2.0.1
Werkzeug==0.9.6
httplib2==0.9
itsdangerous==0.24
oauth==1.0.1
oauth2==1.9.0.post1
uWSGI==2.0.13
urwid==1.2.1
#wsgiref==0.1.2
Sphinx==1.2.3
gunicorn>=19.5.0
pylti>=0.1.3
python-2.7.14
<html>
<head>
<title>Add Problem</title>
</head>
<body>
<form action="{{ url_for('grade') }}" method="post" name="add_form">
{{ form.hidden_tag() }}
<p>
{{ form.p1(readonly=true) }} +
{{ form.p2(readonly=true) }} =
{{ form.result }}
</p>
<p><input type="submit" value="Check"></p>
</form>
</body>
</html>
There was an error
<html>
<head>
<title>Add Problem</title>
</head>
<body>
<form action="{{ url_for('grade') }}" method="post" name="login">
{{ form.hidden_tag() }}
<p>
{{ form.p1(readonly=true) }} +
{{ form.p2(readonly=true) }} =
{{ form.result(readonly=true) }}
</p>
Is correct? {{ form.correct(readonly=true) }}
<p>
<a href="{{ url_for('add_form') }}">New Problem</a>
</p>
<p>
<a href="{{ form.return_url }}">Exit</a>
</p>
</form>
</body>
</html>
<html>
<head>
<title>MIT LTI Flask Sample</title>
</head>
<body>
<h1>Hello {{ lti.session['lis_person_name_full'] }}
({{ lti.nickname }}). </h1 >
<a href="{{ url_for('add_form') }}">Start problem</a>
<br>
<a href="{{ url_for('index_staff') }}">Start staff page</a>
</body>
</html>
<html>
<head>
<title>MIT LTI Flask Sample (staff only)</title>
</head>
<body>
<h1>Hello {{ lti.session['lis_person_name_full'] }}
({{ lti.nickname }}), you are:
{{ lti.role }}</h1>
<a href="{{ url_for('add_form') }}">Start problem</a>
</body>
</html>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment