From 34481e9a110fc4142b2d3e79f918f55b254a975e Mon Sep 17 00:00:00 2001
From: Petr Viktorin <encukou@gmail.com>
Date: Sun, 12 Feb 2012 21:30:30 +0100
Subject: [PATCH] Add Sphinx documentation

---
 doc/Makefile            | 146 +++++++++++++++++++
 doc/conf.py             | 230 +++++++++++++++++++++++++++++
 doc/index.rst           |  25 ++++
 doc/installing.rst      | 156 ++++++++++++++++++++
 doc/main-tables.rst     | 175 ++++++++++++++++++++++
 doc/schema.rst          |   6 +
 doc/usage.rst           | 193 +++++++++++++++++++++++++
 pokedex/doc/__init__.py |   0
 pokedex/doc/tabledoc.py | 313 ++++++++++++++++++++++++++++++++++++++++
 9 files changed, 1244 insertions(+)
 create mode 100644 doc/Makefile
 create mode 100644 doc/conf.py
 create mode 100644 doc/index.rst
 create mode 100644 doc/installing.rst
 create mode 100644 doc/main-tables.rst
 create mode 100644 doc/schema.rst
 create mode 100644 doc/usage.rst
 create mode 100644 pokedex/doc/__init__.py
 create mode 100644 pokedex/doc/tabledoc.py

diff --git a/doc/Makefile b/doc/Makefile
new file mode 100644
index 0000000..39dd586
--- /dev/null
+++ b/doc/Makefile
@@ -0,0 +1,146 @@
+# Makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line.
+SPHINXOPTS    =
+SPHINXBUILD   = sphinx-build
+PAPER         =
+BUILDDIR      = _build
+
+# Internal variables.
+PAPEROPT_a4     = -D latex_paper_size=a4
+PAPEROPT_letter = -D latex_paper_size=letter
+ALLSPHINXOPTS   = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+
+.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest
+
+all: doctest
+	$(MAKE) html coverage
+
+commit:
+	mkdir -p _gh-pages
+	(cd _gh-pages && git init || true)
+	rm -rf _build/html/.git
+	cp -r _gh-pages/.git _build/html
+	(cd _build/html && git add . && git commit -m "sphinx build $$(date --rfc-3339=seconds)" || true)
+	(cd _gh-pages && git pull ../_build/html)
+
+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 "  text       to make text files"
+	@echo "  man        to make manual pages"
+	@echo "  changes    to make an overview of all changed/added/deprecated items"
+	@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
+	touch $(BUILDDIR)/html/.nojekyll
+	@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/pokedex.qhcp"
+	@echo "To view the help file:"
+	@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/pokedex.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/pokedex"
+	@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/pokedex"
+	@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."
+
+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."
+
+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."
+
+coverage:
+	$(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage
+	cat $(BUILDDIR)/coverage/*.txt
diff --git a/doc/conf.py b/doc/conf.py
new file mode 100644
index 0000000..5fcb53a
--- /dev/null
+++ b/doc/conf.py
@@ -0,0 +1,230 @@
+# -*- coding: utf-8 -*-
+#
+# pokedex documentation build configuration file, created by
+# sphinx-quickstart on Tue Apr 12 17:43:05 2011.
+#
+# 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, os
+
+reload(sys)
+sys.setdefaultencoding("UTF-8")
+
+# 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.pngmath',
+        'sphinx.ext.intersphinx',
+        #'sphinx.ext.viewcode',
+        'sphinx.ext.coverage',
+        'pokedex.doc.tabledoc',
+    ]
+
+# 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'
+
+# The master toctree document.
+master_doc = 'index'
+
+# General information about the project.
+project = u'pokedex'
+copyright = u'2011, Alex Munroe (Eevee)'
+
+# 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 = '0.1'
+# The full version, including alpha/beta/rc tags.
+release = '0.1'
+
+# 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 = ['_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 = []
+
+
+intersphinx_mapping = {'sqlalchemy': ('http://www.sqlalchemy.org/docs', None)}
+
+# -- 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 = 'default'
+
+# 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.
+#html_theme_path = []
+
+# 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']
+
+# 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 = False
+
+# 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 = 'pokedexdoc'
+
+
+# -- Options for LaTeX output --------------------------------------------------
+
+# The paper size ('letter' or 'a4').
+#latex_paper_size = 'letter'
+
+# The font size ('10pt', '11pt' or '12pt').
+#latex_font_size = '10pt'
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title, author, documentclass [howto/manual]).
+latex_documents = [
+  ('index', 'pokedex.tex', u'Pokedex Documentation',
+   u'veekun', '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
+
+# Additional stuff for the LaTeX preamble.
+#latex_preamble = ''
+
+# 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', 'pokedex', u'Pokedex Documentation',
+     [u'veekun'], 1)
+]
diff --git a/doc/index.rst b/doc/index.rst
new file mode 100644
index 0000000..a525f6f
--- /dev/null
+++ b/doc/index.rst
@@ -0,0 +1,25 @@
+.. pokedex documentation master file, created by
+   sphinx-quickstart on Tue Apr 12 17:43:05 2011.
+   You can adapt this file completely to your liking, but it should at least
+   contain the root `toctree` directive.
+
+The pokedex documentation
+=========================
+
+Jump right in!
+
+Contents:
+
+.. toctree::
+   :maxdepth: 2
+
+   installing
+   usage
+   schema
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`search`
+
diff --git a/doc/installing.rst b/doc/installing.rst
new file mode 100644
index 0000000..44b94a9
--- /dev/null
+++ b/doc/installing.rst
@@ -0,0 +1,156 @@
+Installing the pokedex library
+==============================
+
+Quick startup with Ubuntu/Debian-like systems
+---------------------------------------------
+
+Run the following from an empty directory::
+
+    $ sudo apt-get install git python python-pip python-sqlalchemy
+    $ git clone git://github.com/veekun/pokedex.git
+    $ pip install -E env -e pokedex
+    $ source env/bin/activate
+    (env)$ pokedex setup -v
+    (env)$ pokedex lookup eevee
+
+If it all goes smoothly, you can now use ``env/bin/pokedex``, the command-line
+tool, and ``env/bin/python``, a Python interpreter configured to use the
+pokedex library.
+
+That is all you need. Feel free to skip the rest of this chapter if you're not
+interested in the details.
+
+Prerequisites
+-------------
+
+Linux
+^^^^^
+
+Ubuntu/Debian users should run the following::
+
+    $ sudo apt-get install git python python-pip
+
+With other Linuxes, install the packages for git, python (2.6 or 2.7,
+*not* 3.x), and python-pip.
+
+If you succeeded, skip the Detailed instructions.
+
+Detailed instructions
+^^^^^^^^^^^^^^^^^^^^^
+
+You should know what a command line is and how to work with it.
+The here we assume you're using Linux [#]_, if that's not the case, make
+sure you have enough computer knowledge to translate the instructions to your
+operating system.
+
+Pokedex is distributed via Git_. So, get Git.
+
+You will also need Python_ 2; the language pokedex is written in. Be sure to get
+version **2.6** or **2.7**. Pokedex does not work with Python 3.x yet, and it
+most likely won't work with 2.5 or earlier.
+
+Next, get pip_, a tool to install Python packages. Experts can use another
+tool, of course.
+
+Make sure git and pip are on your path.
+
+Optionally you can install SQLAlchemy_, `Python markdown`_, Whoosh_,
+or construct_. If you don't, pip will atuomatically download and install a copy
+for you, but some are pretty big so you might want to install it system-wide.
+(Unfortunately, many distros have outdated versions of these libraries, so pip
+will install pokedex's own copy anyway.)
+
+Getting and installing pokedex
+------------------------------
+
+Run the following from an empty directory::
+
+    $ git clone git://git.veekun.com/pokedex.git
+    $ pip install -E env -e pokedex
+
+This will give you two directories: pokedex (containing the source code and
+data), and env (a virtualenv_).
+
+In env/bin, there are three interesting files:
+
+* pokedex: The pokedex program
+* python: A copy of Python that knows about pokedex and its prerequisites.
+* activate: Typing ``source env/bin/activate`` in a shell will put
+  pokedex and our bin/python on the $PATH, and generally set things up to work
+  with them. Your prompt will change to let you know of this. You can end such
+  a session by typing ``deactivate``.
+
+This documentation will assume that you've activated the virtualenv, so
+``pokedex`` means ``env/bin/pokedex``.
+
+Advanced
+^^^^^^^^
+
+You can of course install into an existing virtualenv, by either using its pip
+and leaving out the ``-E env``, or running the setup script directly::
+
+    (anotherenv)$ cd pokedex
+    (anotherenv)pokedex$ python setup.py develop
+
+It is also possible to install pokedex system-wide. There are problems with
+that. Don't do it. The only time you need ``sudo`` is for getting the
+prerequisites.
+
+Loading the database
+--------------------
+
+Before you can do anything useful with pokedex, you need to load the database::
+
+    $ pokedex setup -v
+
+This will load the data into a default SQLite database and create a default
+Whoosh index.
+
+Advanced
+^^^^^^^^
+
+If you want to use another database, make sure you have the corresponding
+`SQLAlchemy engine`_ for it and either use the ``-e`` switch, (e.g.
+``-e postgresql://@/pokedex``), or set the ``POKEDEX_DB_ENGINE`` environment
+variable.
+
+To use another lookup index directory, specify it with ``-i`` or the
+``POKEDEX_INDEX_DIR`` variable.
+
+Make sure you always use the same options whenever you use pokedex.
+
+If you're confused about what pokedex thinks its settings are, check
+``pokedex status``.
+
+See ``pokedex help`` for even more options.
+
+All done
+--------
+
+To verify that all went smoothly, check that the pokedex tool finds your
+favorite pokémon::
+
+    $ pokedex lookup eevee
+
+Yes, that was a bit anti-climatic. The command-line tool doesn't do much,
+currently.
+
+
+
+
+
+
+.. _Git: http://git-scm.com/
+.. _Python: http://www.python.org/
+.. _pip: http://pypi.python.org/pypi/pip
+.. _SQLAlchemy: www.sqlalchemy.org/
+.. _`Python markdown`: http://www.freewisdom.org/projects/python-markdown/
+.. _Whoosh: http://whoosh.ca/
+.. _construct: pypi.python.org/pypi/construct
+.. _virtualenv: http://www.virtualenv.org/en/latest/
+.. _`SQLAlchemy engine`: http://www.sqlalchemy.org/docs/core/engines.html
+
+.. rubric:: Footnotes
+.. [#] If you write instructions for another OS, well be happy to include them
+    here. The reason your OS is not listed here is because the author doesn't
+    use it, so naturally he can't write instructions for it.
diff --git a/doc/main-tables.rst b/doc/main-tables.rst
new file mode 100644
index 0000000..7cef777
--- /dev/null
+++ b/doc/main-tables.rst
@@ -0,0 +1,175 @@
+The pokédex tables
+==================
+
+.. module:: pokedex.db.tables
+
+The :mod:`pokedex.db.tables` module defines all of the tables in the Pokédex.
+They are all defined with SQLAlchemy's
+:mod:`~sqlalchemy.ext.declarative` extension.
+
+To introspect the tables programmatically, you can use the following:
+
+.. data:: mapped_classes
+
+    A list of all the classes you see below.
+
+.. data:: metadata
+
+    The SQLAlchemy :class:`~sqlalchemy.schema.MetaData` containing all the
+    tables.
+
+Each of the classes has a ``translation_classes`` attribute: a potentially
+empty list of translation classes. See :mod:`pokedex.db.multilang` for how
+these work.
+
+Many tables have these columns:
+
+- **id**: An integer primary key. Sometimes it's semantically meaningful, most
+  often it isn't.
+- **identifier**: A string identifier of the class, and the preferred way to
+  access individual items.
+- **name**: A name (uses the multilang functionality)
+
+Pokémon
+-------
+
+.. dex-table:: PokemonSpecies
+.. dex-table:: Pokemon
+.. dex-table:: PokemonForm
+.. dex-table:: EvolutionChain
+.. dex-table:: PokemonEvolution
+
+Moves
+-----
+
+.. dex-table:: Move
+.. dex-table:: MoveEffect
+.. dex-table:: MoveMeta
+
+Items
+-----
+
+.. dex-table:: Item
+.. dex-table:: Berry
+
+Types
+-----
+
+.. dex-table:: Type
+
+Abilities
+---------
+
+.. dex-table:: Ability
+
+Language
+--------
+
+.. dex-table:: Language
+
+Version stuff
+-------------
+
+.. dex-table:: Generation
+.. dex-table:: VersionGroup
+.. dex-table:: Version
+.. dex-table:: Pokedex
+.. dex-table:: Region
+
+Encounters
+----------
+
+.. dex-table:: Location
+.. dex-table:: LocationArea
+.. dex-table:: LocationAreaEncounterRate
+.. dex-table:: Encounter
+.. dex-table:: EncounterCondition
+.. dex-table:: EncounterConditionValue
+.. dex-table:: EncounterMethod
+.. dex-table:: EncounterSlot
+
+
+Contests
+--------
+
+.. dex-table:: ContestCombo
+.. dex-table:: ContestEffect
+.. dex-table:: SuperContestCombo
+.. dex-table:: SuperContestEffect
+
+Enum tables
+-----------
+
+.. dex-table:: BerryFirmness
+.. dex-table:: ContestType
+.. dex-table:: EggGroup
+.. dex-table:: EvolutionTrigger
+.. dex-table:: GrowthRate
+.. dex-table:: ItemCategory
+.. dex-table:: ItemFlingEffect
+.. dex-table:: ItemPocket
+.. dex-table:: MoveBattleStyle
+.. dex-table:: MoveDamageClass
+.. dex-table:: MoveMetaAilment
+.. dex-table:: MoveMetaCategory
+.. dex-table:: MoveTarget
+.. dex-table:: Nature
+.. dex-table:: PokemonColor
+.. dex-table:: PokemonMoveMethod
+.. dex-table:: PokemonShape
+.. dex-table:: Stat
+
+Changelogs
+----------
+
+.. dex-table:: AbilityChangelog
+.. dex-table:: MoveEffectChangelog
+.. dex-table:: MoveChangelog
+
+Flavor text
+-----------
+
+.. dex-table:: ItemFlavorText
+.. dex-table:: AbilityFlavorText
+.. dex-table:: MoveFlavorText
+.. dex-table:: PokemonSpeciesFlavorText
+
+Association tables
+------------------
+
+.. dex-table:: BerryFlavor
+.. dex-table:: EncounterConditionValueMap
+.. dex-table:: ItemFlag
+.. dex-table:: ItemFlagMap
+.. dex-table:: Machine
+.. dex-table:: MoveFlag
+.. dex-table:: MoveFlagMap
+.. dex-table:: MoveMetaStatChange
+.. dex-table:: NatureBattleStylePreference
+.. dex-table:: NaturePokeathlonStat
+.. dex-table:: PokeathlonStat
+.. dex-table:: PokemonAbility
+.. dex-table:: PokemonEggGroup
+.. dex-table:: PokemonFormPokeathlonStat
+.. dex-table:: PokemonHabitat
+.. dex-table:: PokemonMove
+.. dex-table:: PokemonStat
+.. dex-table:: PokemonItem
+.. dex-table:: PokemonType
+.. dex-table:: TypeEfficacy
+.. dex-table:: VersionGroupRegion
+
+Index maps
+----------
+
+.. dex-table:: ItemGameIndex
+.. dex-table:: LocationGameIndex
+.. dex-table:: PokemonDexNumber
+.. dex-table:: PokemonGameIndex
+
+Mics tables
+-----------
+
+.. dex-table:: Experience
+.. dex-table:: StatHint
+
diff --git a/doc/schema.rst b/doc/schema.rst
new file mode 100644
index 0000000..48db3d7
--- /dev/null
+++ b/doc/schema.rst
@@ -0,0 +1,6 @@
+The database schema
+===================
+
+.. toctree::
+
+    main-tables
diff --git a/doc/usage.rst b/doc/usage.rst
new file mode 100644
index 0000000..b4a6ef5
--- /dev/null
+++ b/doc/usage.rst
@@ -0,0 +1,193 @@
+Using pokedex
+=============
+
+The pokédex is, first and foremost, a Python library. To get the most of it,
+you'll need to learn `Python`_ and `SQLAlchemy`_.
+
+Here is a small example of using pokedex:
+
+.. testcode::
+
+    from pokedex.db import connect, tables, util
+    session = connect()
+    pokemon = util.get(session, tables.PokemonSpecies, 'bulbasaur')
+    print u'{0.name}, the {0.genus} Pokemon'.format(pokemon)
+
+Running this will give you some Bulbasaur info:
+
+.. testoutput::
+
+    Bulbasaur, the Seed Pokemon
+
+Connecting
+----------
+
+To get information out of the Pokédex, you will need to create a
+:class:`Session <pokedex.db.multilang.MultilangSession>`. To do that, use
+:func:`pokedex.db.connect`. For simple uses, you don't need to give it any
+arguments: it the database that ``pokedex load`` fills up by default. If you
+need to select another database, give its URI as the first argument.
+
+The object :func:`~pokedex.db.connect` gives you is actually a
+:class:`SQLAlchemy session <sqlalchemy.orm.session.Session>`, giving you the
+full power of SQLAlchemy for working with the data. We'll cover some basics
+here, but if you intend to do some serious work, do read SQLAlchemy's docs.
+
+Pokédex tables
+--------------
+
+Data in the pokédex is organized in tables, defined in
+:mod:`pokedex.db.tables`.
+There is quite a few or them. To get you started, here are a few common ones:
+
+* :class:`~pokedex.db.tables.PokemonSpecies`
+* :class:`~pokedex.db.tables.Move`
+* :class:`~pokedex.db.tables.Item`
+* :class:`~pokedex.db.tables.Type`
+
+Getting things
+--------------
+
+If you know what you want from the pokédex, you can use the
+:func:`pokedex.db.util.get` function. It looks up a thing in a table, based on
+its identifier, name, or ID, and returns it.
+
+.. testcode::
+
+    def print_pokemon(pokemon):
+        print u'{0.name}, the {0.genus} Pokemon'.format(pokemon)
+
+    print_pokemon(util.get(session, tables.PokemonSpecies, identifier='eevee'))
+    print_pokemon(util.get(session, tables.PokemonSpecies, name=u'Ho-Oh'))
+    print_pokemon(util.get(session, tables.PokemonSpecies, id=50))
+
+    def print_item(item):
+        print u'{0.name}: ${0.cost}'.format(item)
+
+    print_item(util.get(session, tables.Item, identifier='great-ball'))
+    print_item(util.get(session, tables.Item, name='Potion'))
+    print_item(util.get(session, tables.Item, id=30))
+
+.. testoutput::
+
+    Eevee, the Evolution Pokemon
+    Ho-Oh, the Rainbow Pokemon
+    Diglett, the Mole Pokemon
+    Great Ball: $600
+    Potion: $300
+    Fresh Water: $200
+
+Querying
+--------
+
+So, how do you get data from the session? You use the session's
+:meth:`~sqlalchemy.orm.session.Session.query` method, and give it a pokédex
+Table as an argument. This will give you a :class:`SQLAlchemy query
+<sqlalchemy.orm.query.Query>`.
+
+Ordering
+^^^^^^^^
+
+As always with SQL, you should not rely on query results being in some
+particular order – unless you have ordered the query first. This means that
+you'll want to sort just about every query you will make.
+
+For example, you can get a list of all pokémon species, sorted by their
+:attr:`~pokedex.db.tables.PokemonSpecies.id`, like so:
+
+.. testcode::
+
+    for pokemon in session.query(tables.PokemonSpecies).order_by(tables.PokemonSpecies.id):
+        print pokemon.name
+
+.. testoutput::
+
+    Bulbasaur
+    Ivysaur
+    Venusaur
+    Charmander
+    Charmeleon
+    ...
+    Keldeo
+    Meloetta
+    Genesect
+
+Or to order by :attr:`~pokedex.db.tables.PokemonSpecies.name`:
+
+.. testcode::
+
+    for pokemon in session.query(tables.PokemonSpecies).order_by(tables.PokemonSpecies.name):
+        print pokemon.name
+
+.. testoutput::
+
+        Abomasnow
+        ...
+        Zweilous
+
+
+Filtering
+^^^^^^^^^
+
+Another major operation on queries is filtering, using the query's
+:meth:`~sqlalchemy.orm.query.Query.filter` or
+:meth:`~sqlalchemy.orm.query.Query.filter_by` methods:
+
+.. testcode::
+
+    for move in session.query(tables.Move).filter(tables.Move.power > 200):
+        print move.name
+
+.. testoutput::
+
+    Explosion
+
+Joining
+^^^^^^^
+
+The final operation we'll cover here is joining other tables to the query,
+using the query's :meth:`~sqlalchemy.orm.query.Query.join`.
+You will usually want to join on a relationship, such as in the following
+example:
+
+.. testcode::
+
+    query = session.query(tables.Move)
+    query = query.join(tables.Move.type)
+    query = query.filter(tables.Type.identifier == 'grass')
+    query = query.filter(tables.Move.power >= 100)
+    query = query.order_by(tables.Move.power)
+    query = query.order_by(tables.Move.name)
+
+    print 'The most powerful Grass-type moves:'
+    for move in query:
+        print u'{0.name} ({0.power})'.format(move)
+
+.. testoutput::
+
+    The most powerful Grass-type moves:
+    Petal Dance (120)
+    Power Whip (120)
+    Seed Flare (120)
+    SolarBeam (120)
+    Wood Hammer (120)
+    Leaf Storm (140)
+    Frenzy Plant (150)
+
+That concludes our brief tutorial.
+If you need to do more, consult the `SQLAlchemy documentation`_.
+
+API documentation
+-----------------
+
+.. autofunction:: pokedex.db.connect
+
+    See :class:`sqlalchemy.orm.session.Session` for more documentation on the
+    returned object.
+
+.. autofunction:: pokedex.db.util.get
+
+
+.. _Python: http://www.python.org
+.. _SQLAlchemy: http://www.sqlalchemy.org
+.. _`SQLAlchemy documentation`: http://www.sqlalchemy.org/docs/orm/tutorial.html
diff --git a/pokedex/doc/__init__.py b/pokedex/doc/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/pokedex/doc/tabledoc.py b/pokedex/doc/tabledoc.py
new file mode 100644
index 0000000..74287ea
--- /dev/null
+++ b/pokedex/doc/tabledoc.py
@@ -0,0 +1,313 @@
+# Encoding: UTF-8
+
+u"""Automatic documentation generation for pokédex tables
+
+This adds a "dex-table" directive to Sphinx, which works like "autoclass",
+but documents Pokédex mapped classes.
+"""
+# XXX: This assumes all the tables are in pokedex.db.tables
+
+import functools
+import textwrap
+
+from docutils import nodes
+from docutils.statemachine import ViewList
+from sphinx.util.compat import Directive, make_admonition
+from sphinx.locale import _
+from sphinx.domains.python import PyClasslike
+from sphinx.util.docfields import Field, GroupedField, TypedField
+from sphinx.ext.autodoc import ClassLevelDocumenter
+
+from sqlalchemy import types
+from sqlalchemy.orm.attributes import InstrumentedAttribute
+from sqlalchemy.orm import configure_mappers
+from sqlalchemy.ext.associationproxy import AssociationProxy
+from pokedex.db.markdown import MoveEffectPropertyMap, MoveEffectProperty
+
+from pokedex.db import tables, markdown
+
+# Make sure all the backrefs are in place
+configure_mappers()
+
+
+column_to_cls = {}
+for cls in tables.mapped_classes:
+    for column in cls.__table__.c:
+        column_to_cls[column] = cls
+
+class dextabledoc(nodes.Admonition, nodes.Element):
+    pass
+
+def visit_todo_node(self, node):
+    self.visit_admonition(node)
+
+def depart_todo_node(self, node):
+    self.depart_admonition(node)
+
+def column_type_str(column):
+    """Extract the type name from a SQLA column
+    """
+    type_ = column.type
+    # We're checking the specific type here: no issubclass
+    if type(type_) in (types.Integer, types.SmallInteger):
+        return 'int'
+    if type(type_) == types.Boolean:
+        return 'bool'
+    if type(type_) == types.Unicode:
+        return u'unicode – %s' % column.info['format']
+    if type(type_) == types.Enum:
+        return 'enum: [%s]' % ', '.join(type_.enums)
+    if type(type_) == markdown.MarkdownColumn:
+        return 'markdown'
+    raise ValueError(repr(type_))
+
+common_columns = 'id identifier name'.split()
+
+def column_header(c, class_name=None, transl_name=None, show_type=True,
+        relation=None, relation_name=None):
+    """Return the column header for the given column"""
+    result = []
+    if relation_name:
+        name = relation_name
+    else:
+        name = c.name
+    if class_name:
+        result.append(u'%s.\ **%s**' % (class_name, name))
+    else:
+        result.append(u'**%s**' % c.name)
+    if c.foreign_keys:
+        for fk in c.foreign_keys:
+            if fk.column in column_to_cls:
+                foreign_cls = column_to_cls[fk.column]
+                if relation_name:
+                    result.append(u'(%s →' % c.name)
+                else:
+                    result.append(u'(→')
+                result.append(u':class:`~pokedex.db.tables.%s`.%s)' % (
+                        foreign_cls.__name__,
+                        fk.column.name
+                    ))
+                break
+    elif show_type:
+        result.append(u'(*%s*)' % column_type_str(c))
+    if transl_name:
+        result.append(u'via *%s*' % transl_name)
+    return ' '.join(result)
+
+
+def with_header(header=None):
+    """Decorator that adds a section header if there's a any output
+
+    The decorated function should yield output lines; if there are any the
+    header gets added.
+    """
+    def wrap(func):
+        @functools.wraps(func)
+        def wrapped(cls, remaining_attrs):
+            result = list(func(cls, remaining_attrs))
+            if result:
+                # Sphinx/ReST doesn't allow "-----" just anywhere :(
+                yield u''
+                yield u'.. raw:: html'
+                yield u''
+                yield u'    <hr>'
+                yield u''
+                if header:
+                    yield header + u':'
+                    yield u''
+                for row in result:
+                    yield row
+        return wrapped
+    return wrap
+
+### Section generation functions
+
+def generate_table_header(cls, remaining_attrs):
+    first_line, sep, next_lines = unicode(cls.__doc__).partition(u'\n')
+    yield first_line
+    for line in textwrap.dedent(next_lines).split('\n'):
+        yield line
+    yield ''
+
+    yield u'Table name: *%s*' % cls.__tablename__
+    try:
+        yield u'(single: *%s*)' % cls.__singlename__
+    except AttributeError:
+        pass
+    yield u''
+
+def generate_common(cls, remaining_attrs):
+    common_col_headers = []
+    for c in cls.__table__.c:
+        if c.name in common_columns:
+            common_col_headers.append(column_header(c, show_type=False))
+            remaining_attrs.remove(c.name)
+    for translation_class in cls.translation_classes:
+        for c in translation_class.__table__.c:
+            if c.name in common_columns:
+                common_col_headers.append(column_header(c, None,
+                        translation_class.__table__.name, show_type=False))
+                remaining_attrs.remove(c.name)
+
+    if common_col_headers:
+        if len(common_col_headers) > 1:
+            common_col_headers[-1] = 'and ' + common_col_headers[-1]
+        if len(common_col_headers) > 2:
+            separator = u', '
+        else:
+            separator = u' '
+        yield u'Has'
+        yield separator.join(common_col_headers) + '.'
+        yield u''
+
+@with_header(u'Columns')
+def generate_columns(cls, remaining_attrs):
+    name = cls.__name__
+    for c in [c for c in cls.__table__.c if c.name not in common_columns]:
+        remaining_attrs.remove(c.name)
+        relation_name = c.name[:-3]
+        if c.name.endswith('_id') and relation_name in remaining_attrs:
+            relation = getattr(cls, relation_name)
+            yield (column_header(c, name,
+                    relation=relation, relation_name=relation_name) + ':')
+            remaining_attrs.remove(relation_name)
+        else:
+            yield column_header(c, name) + ':'
+        yield u''
+        yield u'  ' + unicode(c.info['description'])
+        yield u''
+
+@with_header(u'Internationalized strings')
+def generate_strings(cls, remaining_attrs):
+    for translation_class in cls.translation_classes:
+        for c in translation_class.__table__.c:
+            if 'format' in c.info:
+                remaining_attrs.discard(c.name)
+                remaining_attrs.discard(c.name + '_map')
+                if c.name in common_columns:
+                    continue
+                yield column_header(c, cls.__name__,
+                        translation_class.__table__.name) + ':'
+                yield u''
+                yield u'  ' + unicode(c.info['description'])
+                yield u''
+
+@with_header(u'Relationships')
+def generate_relationships(cls, remaining_attrs):
+    for rel_name in [c for c in cls.relationship_info.get('_order', [])
+            if c in remaining_attrs]:
+        if rel_name in remaining_attrs:
+            info = cls.relationship_info.get(rel_name)
+            yield u'%s.\ **%s**' % (cls.__name__, rel_name)
+            class_name = u':class:`~pokedex.db.tables.%s`' % info['argument'].__name__
+            if info.get('uselist', True):
+                class_name = u'[%s]' % class_name
+            yield u'(→ %s)' % class_name
+            if 'description' in info:
+                yield u''
+                yield u'  ' + unicode(info['description'])
+            '''
+            if info.get('secondary') is not None:
+                yield u''
+                yield '  Association table: ``%s``' % info['secondary']
+            if 'primaryjoin' in info:
+                yield u'')
+                yield '  Join condition: ``%s``' % info['primaryjoin']
+                if 'secondaryjoin' in info:
+                    yield '  , ``%s``' % info['secondaryjoin']
+            '''
+            if 'order_by' in info:
+                yield u''
+                try:
+                    order = iter(info['order_by'])
+                except TypeError:
+                    order = [info['order_by']]
+                yield u'  '
+                yield '  Ordered by: ' + u', '.join(
+                        u'``%s``' % o for o in order)
+            yield u''
+            remaining_attrs.remove(rel_name)
+
+@with_header(u'Undocumented')
+def generate_undocumented(cls, remaining_attrs):
+    for c in sorted([c for c in remaining_attrs if isinstance(getattr(cls, c),
+            (InstrumentedAttribute, AssociationProxy,
+                MoveEffectPropertyMap, MoveEffectProperty))]):
+        yield u''
+        yield u'%s.\ **%s**' % (cls.__name__, c)
+        remaining_attrs.remove(c)
+
+@with_header(None)
+def generate_other(cls, remaining_attrs):
+    for c in sorted(remaining_attrs):
+        yield u''
+        member = getattr(cls, c)
+        if callable(member):
+            yield '.. automethod:: %s.%s' % (cls.__name__, c)
+        else:
+            yield '.. autoattribute:: %s.%s' % (cls.__name__, c)
+        yield u''
+    remaining_attrs.clear()
+
+
+class DexTable(PyClasslike):
+    """The actual Sphinx documentation generation whatchamacallit
+    """
+    doc_field_types = [
+        TypedField('field', label='Fields',
+            typerolename='obj', typenames=('fieldname', 'type')),
+        ]
+
+    def get_signature_prefix(self, sig):
+        return ''
+        #return u'mapped class '
+
+    def run(self):
+        section = nodes.section()
+        super_result = super(DexTable, self).run()
+        title_text = self.names[0][0]
+        section += nodes.title(text=title_text)
+        section += super_result
+        section['ids'] = ['dex-table-%s' % title_text.lower()]
+        return [section]
+
+    def before_content(self):
+        name = self.names[0][0]
+        for cls in tables.mapped_classes:
+            if name == cls.__name__:
+                break
+        else:
+            raise ValueError('Table %s not found' % name)
+        table = cls.__table__
+
+        remaining_attrs = set(x for x in dir(cls) if not x.startswith('_'))
+        remaining_attrs.difference_update(['metadata', 'translation_classes',
+                'add_relationships', 'relationship_info', 'summary_column'])
+        for transl_class in cls.translation_classes:
+            remaining_attrs.difference_update([
+                    transl_class.relation_name,
+                    transl_class.relation_name + '_table',
+                    transl_class.relation_name + '_local',
+                ])
+
+        generated_content = []  # Just a list of lines!
+
+        generated_content.extend(generate_table_header(cls, remaining_attrs))
+        generated_content.extend(generate_common(cls, remaining_attrs))
+        generated_content.extend(generate_columns(cls, remaining_attrs))
+        generated_content.extend(generate_strings(cls, remaining_attrs))
+        generated_content.extend(generate_relationships(cls, remaining_attrs))
+        generated_content.extend(generate_undocumented(cls, remaining_attrs))
+        generated_content.extend(generate_other(cls, remaining_attrs))
+
+        generated_content.append(u'')
+        self.content = ViewList(generated_content + list(self.content))
+        return super(DexTable, self).before_content()
+
+    def get_index_text(self, modname, name_cls):
+        return '%s (mapped class)' % name_cls[0]
+
+def setup(app):
+    app.add_directive('dex-table', DexTable)
+
+    # XXX: Specify that this depends on pokedex.db.tables ...?