Merge branch 'doc'

This commit is contained in:
Andrew Ekstedt 2012-06-08 23:32:31 -07:00
commit 2af1d5f428
12 changed files with 1287 additions and 18 deletions

146
doc/Makefile Normal file
View file

@ -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

230
doc/conf.py Normal file
View file

@ -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)
]

25
doc/index.rst Normal file
View file

@ -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`

156
doc/installing.rst Normal file
View file

@ -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.

179
doc/main-tables.rst Normal file
View file

@ -0,0 +1,179 @@
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:: PalParkArea
.. 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:: VersionGroupPokemonMoveMethod
.. dex-table:: VersionGroupRegion
Index maps
----------
.. dex-table:: ItemGameIndex
.. dex-table:: LocationGameIndex
.. dex-table:: PokemonDexNumber
.. dex-table:: PokemonFormGeneration
.. dex-table:: PokemonGameIndex
Mics tables
-----------
.. dex-table:: Experience
.. dex-table:: PalPark
.. dex-table:: StatHint

6
doc/schema.rst Normal file
View file

@ -0,0 +1,6 @@
The database schema
===================
.. toctree::
main-tables

193
doc/usage.rst Normal file
View file

@ -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

View file

@ -118,6 +118,7 @@ def create_translation_table(_table_name, foreign_class, relation_name,
Translations = type(_table_name, (object,), { Translations = type(_table_name, (object,), {
'_language_identifier': association_proxy('local_language', 'identifier'), '_language_identifier': association_proxy('local_language', 'identifier'),
'relation_name': relation_name,
}) })
# Create the table object # Create the table object

View file

@ -1272,6 +1272,7 @@ class PokemonForm(TableBase):
@property @property
def name(self): def name(self):
"""Name of this form: the form_name, if set; otherwise the species name"""
return self.pokemon_name or self.species.name return self.pokemon_name or self.species.name
create_translation_table('pokemon_form_names', PokemonForm, 'names', create_translation_table('pokemon_form_names', PokemonForm, 'names',
@ -1705,7 +1706,6 @@ ContestCombo.second = relationship(Move,
innerjoin=True, lazy='joined', innerjoin=True, lazy='joined',
backref='contest_combo_second') backref='contest_combo_second')
Encounter.condition_value_map = relationship(EncounterConditionValueMap, Encounter.condition_value_map = relationship(EncounterConditionValueMap,
backref='encounter') backref='encounter')
Encounter.condition_values = association_proxy('condition_value_map', 'condition_value') Encounter.condition_values = association_proxy('condition_value_map', 'condition_value')
@ -1954,10 +1954,8 @@ Pokemon.all_abilities = relationship(Ability,
secondary=PokemonAbility.__table__, secondary=PokemonAbility.__table__,
order_by=PokemonAbility.slot.asc(), order_by=PokemonAbility.slot.asc(),
innerjoin=True, innerjoin=True,
backref=backref('all_pokemon', backref=backref('all_pokemon', order_by=Pokemon.order.asc()),
order_by=Pokemon.order.asc(), doc=u"All abilities the Pokémon can have, including the Hidden Ability")
),
)
Pokemon.abilities = relationship(Ability, Pokemon.abilities = relationship(Ability,
secondary=PokemonAbility.__table__, secondary=PokemonAbility.__table__,
primaryjoin=and_( primaryjoin=and_(
@ -1966,10 +1964,8 @@ Pokemon.abilities = relationship(Ability,
), ),
innerjoin=True, innerjoin=True,
order_by=PokemonAbility.slot.asc(), order_by=PokemonAbility.slot.asc(),
backref=backref('pokemon', backref=backref('pokemon', order_by=Pokemon.order.asc()),
order_by=Pokemon.order.asc(), doc=u"Abilities the Pokémon can have in the wild")
),
)
Pokemon.dream_ability = relationship(Ability, Pokemon.dream_ability = relationship(Ability,
secondary=PokemonAbility.__table__, secondary=PokemonAbility.__table__,
primaryjoin=and_( primaryjoin=and_(
@ -1977,10 +1973,8 @@ Pokemon.dream_ability = relationship(Ability,
PokemonAbility.is_dream == True, PokemonAbility.is_dream == True,
), ),
uselist=False, uselist=False,
backref=backref('dream_pokemon', backref=backref('dream_pokemon', order_by=Pokemon.order),
order_by=Pokemon.order, doc=u"The Pokémon's Hidden Ability")
),
)
Pokemon.forms = relationship(PokemonForm, Pokemon.forms = relationship(PokemonForm,
primaryjoin=Pokemon.id==PokemonForm.pokemon_id, primaryjoin=Pokemon.id==PokemonForm.pokemon_id,
order_by=(PokemonForm.order.asc(), PokemonForm.form_identifier.asc()), order_by=(PokemonForm.order.asc(), PokemonForm.form_identifier.asc()),
@ -1989,9 +1983,11 @@ Pokemon.default_form = relationship(PokemonForm,
primaryjoin=and_( primaryjoin=and_(
Pokemon.id==PokemonForm.pokemon_id, Pokemon.id==PokemonForm.pokemon_id,
PokemonForm.is_default==True), PokemonForm.is_default==True),
uselist=False, lazy='joined') uselist=False, lazy='joined',
doc=u"A representative form of this pokémon")
Pokemon.items = relationship(PokemonItem, Pokemon.items = relationship(PokemonItem,
backref='pokemon') backref='pokemon',
doc=u"Info about items this pokémon holds in the wild")
Pokemon.stats = relationship(PokemonStat, Pokemon.stats = relationship(PokemonStat,
innerjoin=True, innerjoin=True,
order_by=PokemonStat.stat_id.asc(), order_by=PokemonStat.stat_id.asc(),
@ -2077,7 +2073,9 @@ PokemonStat.stat = relationship(Stat,
PokemonSpecies.parent_species = relationship(PokemonSpecies, PokemonSpecies.parent_species = relationship(PokemonSpecies,
primaryjoin=PokemonSpecies.evolves_from_species_id==PokemonSpecies.id, primaryjoin=PokemonSpecies.evolves_from_species_id==PokemonSpecies.id,
remote_side=[PokemonSpecies.id], remote_side=[PokemonSpecies.id],
backref='child_species') backref=backref('child_species',
doc=u"The species to which this one evolves"),
doc=u"The species from which this one evolves")
PokemonSpecies.evolutions = relationship(PokemonEvolution, PokemonSpecies.evolutions = relationship(PokemonEvolution,
primaryjoin=PokemonSpecies.id==PokemonEvolution.evolved_species_id, primaryjoin=PokemonSpecies.id==PokemonEvolution.evolved_species_id,
backref=backref('evolved_species', innerjoin=True, lazy='joined')) backref=backref('evolved_species', innerjoin=True, lazy='joined'))
@ -2108,7 +2106,8 @@ PokemonSpecies.default_form = relationship(PokemonForm,
Pokemon.is_default==True), Pokemon.is_default==True),
secondaryjoin=and_(Pokemon.id==PokemonForm.pokemon_id, secondaryjoin=and_(Pokemon.id==PokemonForm.pokemon_id,
PokemonForm.is_default==True), PokemonForm.is_default==True),
uselist=False) uselist=False,
doc=u"A representative form of this species")
PokemonSpecies.default_pokemon = relationship(Pokemon, PokemonSpecies.default_pokemon = relationship(Pokemon,
primaryjoin=and_( primaryjoin=and_(
PokemonSpecies.id==Pokemon.species_id, PokemonSpecies.id==Pokemon.species_id,

0
pokedex/doc/__init__.py Normal file
View file

334
pokedex/doc/tabledoc.py Normal file
View file

@ -0,0 +1,334 @@
# 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.properties import RelationshipProperty
from sqlalchemy.orm import Mapper, 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 and relation_name + '_id' == c.name:
result.append(u'(%s' % c.name)
elif 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''
yield u'Primary key: %s.' % u', '.join(
u'**%s**' % col.key for col in cls.__table__.primary_key.columns)
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):
def isrelationship(prop):
return isinstance(prop, InstrumentedAttribute) and isinstance(prop.property, RelationshipProperty)
for attr_name in sorted(remaining_attrs):
prop = getattr(cls, attr_name)
if not isrelationship(prop):
continue
rel = prop.property
yield u'%s.\ **%s**' % (cls.__name__, attr_name)
class_name = u':class:`~pokedex.db.tables.%s`' % rel.mapper.class_.__name__
if rel.uselist:
class_name = u'[%s]' % class_name
yield u'(→ %s)' % class_name
if rel.doc:
yield u''
yield u' ' + unicode(rel.doc)
if rel.secondary is not None:
yield u''
yield ' Association table: ``%s``' % rel.secondary
#if rel.primaryjoin is not None:
# yield u''
# yield ' Join condition: ``%s``' % rel.primaryjoin
# if rel.secondaryjoin is not None:
# yield ' , ``%s``' % rel.secondaryjoin
if rel.order_by:
yield u''
yield u' '
yield ' Ordered by: ' + u', '.join(
u'``%s``' % o for o in rel.order_by)
yield u''
remaining_attrs.remove(attr_name)
@with_header(u'Association Proxies')
def generate_associationproxies(cls, remaining_attrs):
for attr_name in sorted(remaining_attrs):
prop = getattr(cls, attr_name)
if isinstance(prop, AssociationProxy):
yield u'%s.\ **%s**:' % (cls.__name__, attr_name)
yield '``{prop.remote_attr.key}`` of ``self.{prop.local_attr.key}``'.format(
prop=prop)
'''if 'description' in info:
yield u''
yield u' ' + unicode(info['description'])'''
yield u''
remaining_attrs.remove(attr_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', '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_associationproxies(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 ...?

View file

@ -8,7 +8,7 @@ setup(
'pokedex': ['data/csv/*.csv'] 'pokedex': ['data/csv/*.csv']
}, },
install_requires=[ install_requires=[
'SQLAlchemy>=0.7', 'SQLAlchemy>=0.7.3',
'whoosh>=2.2.2', 'whoosh>=2.2.2',
'markdown', 'markdown',
'construct', 'construct',