gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[GNUnet-SVN] [taler-bank] branch stable updated (a5263a5 -> 8c2d25b)


From: gnunet
Subject: [GNUnet-SVN] [taler-bank] branch stable updated (a5263a5 -> 8c2d25b)
Date: Fri, 29 Mar 2019 18:08:46 +0100

This is an automated email from the git hooks/post-receive script.

marcello pushed a change to branch stable
in repository bank.

 discard a5263a5  #5546
 discard 8ae219d  syntax
 discard 34c2d4d  syntax
 discard 4d13a50  5543.  Let Jinja render abnormal balances.
 discard e360ce7  Addressing #5543.
 discard adf030a  Addressing #5543.
 discard b537bd2  Addressing #5543.
 discard e5e6d7c  typo
 discard ce60f1d  providing the 'hint' field to those exeptions missing it.
 discard 72e9724  Handling new exeption within the middleware.
 discard 88049fa  Minor change for #5542.
 discard 3573d82  Hot-addressing #5542.
     add 98812fe  fix -c option
     add 4e50c8d  jinja2 was missing in setup.py
     add 4269684  fix unseen leftover.
     add 5f50d22  get rid of old migrations, and keeping their squashed version.
     add 97974b9  readme
     add 686eb78  add missing files to EXTRA_DIST
     add e964f7d  Revert "add missing files to EXTRA_DIST"
     add 7b44e65  fix failing test cases by clearing database in setUp hook
     add abbd804  remove old dep in requirements
     add 8e4ad97  version 0.5.1
     add 984541a  address #5313
     add 0ff63bf  check if parsed numbers do not exceed 2^53 - 1, as for the 
well known JavaScript limit.
     add d671ab6  tilde expansion.
     add 5ff6219  Double-check 'hint' field exists.
     add 11218d4  Restore back-office link
     add bfda2ed  Fix #5414 (always descending order for public histories).
     add c1c942b  fix testcases.
     add ef4a846  Paginator.
     add 2e90943  History element order.
     add 8243ff3  History order.
     add 8e11fbb  Adapt to latest Django changes.
     add 40aca5a  Fix Referer:-header issue.
     add 6806eed  Giving 'corsheader' more precedence.
     add 6879ec9  Do NOT use django-cors-headers.
     add 8b23f1e  remove cors dependency from installed packages.
     add df6d892  Addressing #5497.
     add b6c7d60  Reproducing error from #5499.
     add 645dd65  Fix #5499 (at the test case level); UI to be tested.
     add 36197b2  /history API refactoring.
     add addbc2f  Provide flag to set /history element order (asc|desc).
     add 92b45b2  Give very big initial amount to Survey account.
     add bc03f67  Make big donation just once!
     add 279908e  Implement config @address@hidden
     add 93325d2  Allowing absolute paths for @address@hidden
     add 6789efe  Forbid unknown currencies to enter the database.
     add a3edc34  Fix #5561.
     add 354ba98  4809.
     add 0f26c76  Update submodule
     add b0f9913  Custom 404 page.
     add 652e1f1  Closing #5593.
     add b06a6f7  Starting Doxygen transition.
     add ef29279  Doxyfile
     add b8761e7  gitignore
     add eec9e0e  instruct doxygen to crawl subdirectories
     add b050cbf  Doxygen-commenting views.py.
     add 79b3070  Doxygen-commenting models.py.
     add 668d93c  fix
     add 7591fd5  Doxygen-commenting some of schemas.py.
     add 1af2fd5  Doxygen-commenting schemas.py.
     add f360e73  Doxygen-commenting amounts.py.
     add 9bec042  Doxygen-commenting some of middleware.py.
     add f662e6c  Finish Doxygen-commenting middleware.py.
     add f3e3066  Doxygen-commenting jinja2.py.
     add 575666c  Doxygen-commenting CLI utility 'provide_accounts'.
     add d531d61  Doxygen-commenting dump_talerdb.py.
     add 4e39c44  Doxygen-commenting the CLI utility 'wire_transfer'.
     add 5b39318  Comment setup.py.
     add c2ae44a  Doxygen-commenting the config logic.
     add 0d03c67  Remove duplicate instructions.
     add 35ee6d9  Make *.in files Doxygenated
     add 4d5394f  Doxygen.
     add 5270acd  Exclude the Makefile.in from Doxygenation.
     add 4e6f274  Make function's argument default as URL's.
     add be54c58  Add a .dir-locals,el for configuring emacs for this project
     add 105f09c  Fix #5636.
     add a3e7f9d  "/history" new policy for  'start' default.
     add eadac43  Hot-addressing #5542.
     add c4a08b5  Minor change for #5542.
     add a475bd0  Handling new exeption within the middleware.
     add 1192d66  providing the 'hint' field to those exeptions missing it.
     add 87378c6  typo
     add 5bc40f6  Addressing #5543.
     add 10c96a7  Addressing #5543.
     add 95bf6b0  Addressing #5543.
     add efd3fa0  5543.  Let Jinja render abnormal balances.
     add 4bef7eb  syntax
     add 0d43973  syntax
     add 8c2d25b  #5546

This update added new revisions after undoing existing revisions.
That is to say, some revisions that were in the old version of the
branch are not in the new version.  This situation occurs
when a user --force pushes a change and generates a repository
containing something like this:

 * -- * -- B -- O -- O -- O   (a5263a5)
            \
             N -- N -- N   refs/heads/stable (8c2d25b)

You should already have received notification emails for all of the O
revisions, and so the following emails describe only the N revisions
from the common base, B.

Any revisions marked "omit" are not gone; other references still
refer to them.  Any revisions marked "discard" are gone forever.

No new revisions were added by this update.

Summary of changes:
 .dir-locals.el                                     |  16 +
 .gitignore                                         |   1 +
 .gitmodules                                        |   7 +-
 ChangeLog                                          |   3 +
 Doxyfile                                           | 331 +++++++++++++++++
 README                                             |   6 -
 configure.ac                                       |   2 +-
 requirements.txt                                   |   1 -
 setup.py                                           |   8 +-
 taler-bank-manage.in                               |  63 +++-
 talerbank/app/amount.py                            | 125 ++++++-
 talerbank/app/management/commands/dump_talerdb.py  |  28 +-
 .../app/management/commands/provide_accounts.py    |  52 ++-
 talerbank/app/management/commands/wire_transfer.py |  32 +-
 talerbank/app/middleware.py                        |  57 ++-
 ...shed_0013_remove_banktransaction_reimburses.py} |  35 +-
 .../app/migrations/0002_bankaccount_amount.py      |  21 --
 .../app/migrations/0003_auto_20171030_1346.py      |  21 --
 .../app/migrations/0004_auto_20171030_1428.py      |  34 --
 .../0005_remove_banktransaction_currency.py        |  19 -
 .../app/migrations/0006_auto_20171031_0823.py      |  21 --
 .../app/migrations/0007_auto_20171031_0906.py      |  21 --
 .../app/migrations/0008_auto_20171031_0938.py      |  31 --
 .../app/migrations/0009_auto_20171120_1642.py      |  20 -
 .../migrations/0010_banktransaction_cancelled.py   |  20 -
 .../migrations/0011_banktransaction_reimburses.py  |  19 -
 .../app/migrations/0012_auto_20171212_1540.py      |  19 -
 .../0013_remove_banktransaction_reimburses.py      |  17 -
 talerbank/app/models.py                            |  66 +++-
 talerbank/app/schemas.py                           | 141 +++++--
 talerbank/app/static/web-common                    |   2 +-
 .../templates/{error_exchange.html => 404.html}    |  11 +-
 talerbank/app/templates/base.html                  |   1 +
 talerbank/app/templates/login.html                 |  19 -
 talerbank/app/tests.py                             |  90 ++---
 talerbank/app/urls.py                              |   7 +-
 talerbank/app/views.py                             | 409 ++++++++++++++++-----
 talerbank/jinja2.py                                |  74 +++-
 talerbank/settings.py                              |   6 +-
 talerbank/talerconfig.py                           | 304 ++++++++++++---
 40 files changed, 1566 insertions(+), 594 deletions(-)
 create mode 100644 .dir-locals.el
 create mode 100644 Doxyfile
 rename talerbank/app/migrations/{0001_initial.py => 
0001_squashed_0013_remove_banktransaction_reimburses.py} (52%)
 delete mode 100644 talerbank/app/migrations/0002_bankaccount_amount.py
 delete mode 100644 talerbank/app/migrations/0003_auto_20171030_1346.py
 delete mode 100644 talerbank/app/migrations/0004_auto_20171030_1428.py
 delete mode 100644 
talerbank/app/migrations/0005_remove_banktransaction_currency.py
 delete mode 100644 talerbank/app/migrations/0006_auto_20171031_0823.py
 delete mode 100644 talerbank/app/migrations/0007_auto_20171031_0906.py
 delete mode 100644 talerbank/app/migrations/0008_auto_20171031_0938.py
 delete mode 100644 talerbank/app/migrations/0009_auto_20171120_1642.py
 delete mode 100644 talerbank/app/migrations/0010_banktransaction_cancelled.py
 delete mode 100644 talerbank/app/migrations/0011_banktransaction_reimburses.py
 delete mode 100644 talerbank/app/migrations/0012_auto_20171212_1540.py
 delete mode 100644 
talerbank/app/migrations/0013_remove_banktransaction_reimburses.py
 copy talerbank/app/templates/{error_exchange.html => 404.html} (85%)

diff --git a/.dir-locals.el b/.dir-locals.el
new file mode 100644
index 0000000..6878638
--- /dev/null
+++ b/.dir-locals.el
@@ -0,0 +1,16 @@
+;; Per-directory local variables for GNU Emacs 23 and later.
+
+((nil
+  . ((fill-column . 78)
+     (tab-width   .  4)
+     (indent-tabs-mode . nil)
+     (show-trailing-whitespace . t)
+     (c-basic-offset . 2)
+     (ispell-check-comments . exclusive)
+     (ispell-local-dictionary . "american")
+     (safe-local-variable-values
+         '((c-default-style . "gnu")
+           (sentence-end-double-space . f)
+        (eval add-hook 'prog-mode-hook #'flyspell-prog-mode)
+        (flyspell-issue-message-flag . f) ; avoid messages for every word
+        )))))
diff --git a/.gitignore b/.gitignore
index 6fe5135..700166e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
+doxygen-doc/
 compile
 missing
 install-sh
diff --git a/.gitmodules b/.gitmodules
index 4998965..69b752a 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,4 +1,5 @@
 [submodule "talerbank/app/static/web-common"]
-       path = talerbank/app/static/web-common
-       url = git://taler.net/web-common
-       branch = master
+        path = talerbank/app/static/web-common
+        url = git://git.taler.net/web-common
+        branch = master
+        ignore = dirty
diff --git a/ChangeLog b/ChangeLog
index add810a..1904c20 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,6 @@
+Tue Apr 10 02:39:19 CEST 2018
+       Releasing taler-bank-0.5.1 -FD
+
 Tue Feb 13 10:05:06 CET 2018
         Introducing the /reject REST API, internal wire
         transfers between users, and a middleware-based
diff --git a/Doxyfile b/Doxyfile
new file mode 100644
index 0000000..c5f777d
--- /dev/null
+++ b/Doxyfile
@@ -0,0 +1,331 @@
+# Doxyfile 1.8.13
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+DOXYFILE_ENCODING      = UTF-8
+PROJECT_NAME           = "taler-demobank"
+PROJECT_NUMBER         =
+PROJECT_BRIEF          =
+PROJECT_LOGO           =
+OUTPUT_DIRECTORY       = doxygen-doc/
+CREATE_SUBDIRS         = yes
+ALLOW_UNICODE_NAMES    = NO
+OUTPUT_LANGUAGE        = English
+BRIEF_MEMBER_DESC      = YES
+REPEAT_BRIEF           = YES
+ABBREVIATE_BRIEF       = "The $name class" \
+                         "The $name widget" \
+                         "The $name file" \
+                         is \
+                         provides \
+                         specifies \
+                         contains \
+                         represents \
+                         a \
+                         an \
+                         the
+ALWAYS_DETAILED_SEC    = NO
+INLINE_INHERITED_MEMB  = NO
+FULL_PATH_NAMES        = YES
+STRIP_FROM_PATH        =
+STRIP_FROM_INC_PATH    =
+SHORT_NAMES            = NO
+JAVADOC_AUTOBRIEF      = NO
+QT_AUTOBRIEF           = NO
+MULTILINE_CPP_IS_BRIEF = NO
+INHERIT_DOCS           = YES
+SEPARATE_MEMBER_PAGES  = NO
+TAB_SIZE               = 4
+ALIASES                =
+TCL_SUBST              =
+OPTIMIZE_OUTPUT_FOR_C  = NO
+OPTIMIZE_OUTPUT_JAVA   = NO
+OPTIMIZE_FOR_FORTRAN   = NO
+OPTIMIZE_OUTPUT_VHDL   = NO
+EXTENSION_MAPPING      = in=Python
+MARKDOWN_SUPPORT       = YES
+TOC_INCLUDE_HEADINGS   = 0
+AUTOLINK_SUPPORT       = YES
+BUILTIN_STL_SUPPORT    = NO
+CPP_CLI_SUPPORT        = NO
+SIP_SUPPORT            = NO
+IDL_PROPERTY_SUPPORT   = YES
+DISTRIBUTE_GROUP_DOC   = NO
+GROUP_NESTED_COMPOUNDS = NO
+SUBGROUPING            = YES
+INLINE_GROUPED_CLASSES = NO
+INLINE_SIMPLE_STRUCTS  = NO
+TYPEDEF_HIDES_STRUCT   = NO
+LOOKUP_CACHE_SIZE      = 0
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+EXTRACT_ALL            = YES
+EXTRACT_PRIVATE        = NO
+EXTRACT_PACKAGE        = NO
+EXTRACT_STATIC         = NO
+EXTRACT_LOCAL_CLASSES  = YES
+EXTRACT_LOCAL_METHODS  = NO
+EXTRACT_ANON_NSPACES   = NO
+HIDE_UNDOC_MEMBERS     = NO
+HIDE_UNDOC_CLASSES     = NO
+HIDE_FRIEND_COMPOUNDS  = NO
+HIDE_IN_BODY_DOCS      = NO
+INTERNAL_DOCS          = NO
+CASE_SENSE_NAMES       = YES
+HIDE_SCOPE_NAMES       = NO
+HIDE_COMPOUND_REFERENCE= NO
+SHOW_INCLUDE_FILES     = YES
+SHOW_GROUPED_MEMB_INC  = NO
+FORCE_LOCAL_INCLUDES   = NO
+INLINE_INFO            = YES
+SORT_MEMBER_DOCS       = YES
+SORT_BRIEF_DOCS        = NO
+SORT_MEMBERS_CTORS_1ST = NO
+SORT_GROUP_NAMES       = NO
+SORT_BY_SCOPE_NAME     = NO
+STRICT_PROTO_MATCHING  = NO
+GENERATE_TODOLIST      = YES
+GENERATE_TESTLIST      = YES
+GENERATE_BUGLIST       = YES
+GENERATE_DEPRECATEDLIST= YES
+ENABLED_SECTIONS       =
+MAX_INITIALIZER_LINES  = 30
+SHOW_USED_FILES        = YES
+SHOW_FILES             = YES
+SHOW_NAMESPACES        = NO
+FILE_VERSION_FILTER    =
+LAYOUT_FILE            =
+CITE_BIB_FILES         =
+#---------------------------------------------------------------------------
+# Configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+QUIET                  = NO
+WARNINGS               = YES
+WARN_IF_UNDOCUMENTED   = YES
+WARN_IF_DOC_ERROR      = YES
+WARN_NO_PARAMDOC       = NO
+WARN_AS_ERROR          = NO
+WARN_FORMAT            = "$file:$line: $text"
+WARN_LOGFILE           =
+#---------------------------------------------------------------------------
+# Configuration options related to the input files
+#---------------------------------------------------------------------------
+INPUT                  =
+INPUT_ENCODING         = UTF-8
+FILE_PATTERNS          = *.py *.in
+RECURSIVE              = YES
+EXCLUDE                = Makefile.in
+EXCLUDE_SYMLINKS       = NO
+EXCLUDE_PATTERNS       =
+EXCLUDE_SYMBOLS        =
+EXAMPLE_PATH           =
+EXAMPLE_PATTERNS       = *
+EXAMPLE_RECURSIVE      = NO
+IMAGE_PATH             =
+INPUT_FILTER           =
+FILTER_PATTERNS        =
+FILTER_SOURCE_FILES    = NO
+FILTER_SOURCE_PATTERNS =
+USE_MDFILE_AS_MAINPAGE =
+#---------------------------------------------------------------------------
+# Configuration options related to source browsing
+#---------------------------------------------------------------------------
+SOURCE_BROWSER         = NO
+INLINE_SOURCES         = NO
+STRIP_CODE_COMMENTS    = YES
+REFERENCED_BY_RELATION = NO
+REFERENCES_RELATION    = NO
+REFERENCES_LINK_SOURCE = YES
+SOURCE_TOOLTIPS        = YES
+USE_HTAGS              = NO
+VERBATIM_HEADERS       = YES
+CLANG_ASSISTED_PARSING = NO
+CLANG_OPTIONS          =
+#---------------------------------------------------------------------------
+# Configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+ALPHABETICAL_INDEX     = YES
+COLS_IN_ALPHA_INDEX    = 5
+IGNORE_PREFIX          =
+#---------------------------------------------------------------------------
+# Configuration options related to the HTML output
+#---------------------------------------------------------------------------
+GENERATE_HTML          = YES
+HTML_OUTPUT            = html
+HTML_FILE_EXTENSION    = .html
+HTML_HEADER            =
+HTML_FOOTER            =
+HTML_STYLESHEET        =
+HTML_EXTRA_STYLESHEET  =
+HTML_EXTRA_FILES       =
+HTML_COLORSTYLE_HUE    = 220
+HTML_COLORSTYLE_SAT    = 100
+HTML_COLORSTYLE_GAMMA  = 80
+HTML_TIMESTAMP         = NO
+HTML_DYNAMIC_SECTIONS  = NO
+HTML_INDEX_NUM_ENTRIES = 100
+GENERATE_DOCSET        = NO
+DOCSET_FEEDNAME        = "Doxygen generated docs"
+DOCSET_BUNDLE_ID       = org.doxygen.Project
+DOCSET_PUBLISHER_ID    = org.doxygen.Publisher
+DOCSET_PUBLISHER_NAME  = Publisher
+GENERATE_HTMLHELP      = NO
+CHM_FILE               =
+HHC_LOCATION           =
+GENERATE_CHI           = NO
+CHM_INDEX_ENCODING     =
+BINARY_TOC             = NO
+TOC_EXPAND             = NO
+GENERATE_QHP           = NO
+QCH_FILE               =
+QHP_NAMESPACE          = org.doxygen.Project
+QHP_VIRTUAL_FOLDER     = doc
+QHP_CUST_FILTER_NAME   =
+QHP_CUST_FILTER_ATTRS  =
+QHP_SECT_FILTER_ATTRS  =
+QHG_LOCATION           =
+GENERATE_ECLIPSEHELP   = NO
+ECLIPSE_DOC_ID         = org.doxygen.Project
+DISABLE_INDEX          = NO
+GENERATE_TREEVIEW      = NO
+ENUM_VALUES_PER_LINE   = 4
+TREEVIEW_WIDTH         = 250
+EXT_LINKS_IN_WINDOW    = NO
+FORMULA_FONTSIZE       = 10
+FORMULA_TRANSPARENT    = YES
+USE_MATHJAX            = NO
+MATHJAX_FORMAT         = HTML-CSS
+MATHJAX_RELPATH        = http://cdn.mathjax.org/mathjax/latest
+MATHJAX_EXTENSIONS     =
+MATHJAX_CODEFILE       =
+SEARCHENGINE           = YES
+SERVER_BASED_SEARCH    = NO
+EXTERNAL_SEARCH        = NO
+SEARCHENGINE_URL       =
+SEARCHDATA_FILE        = searchdata.xml
+EXTERNAL_SEARCH_ID     =
+EXTRA_SEARCH_MAPPINGS  =
+#---------------------------------------------------------------------------
+# Configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+GENERATE_LATEX         = NO
+LATEX_OUTPUT           = latex
+LATEX_CMD_NAME         = latex
+MAKEINDEX_CMD_NAME     = makeindex
+COMPACT_LATEX          = NO
+PAPER_TYPE             = a4
+EXTRA_PACKAGES         =
+LATEX_HEADER           =
+LATEX_FOOTER           =
+LATEX_EXTRA_STYLESHEET =
+LATEX_EXTRA_FILES      =
+PDF_HYPERLINKS         = YES
+USE_PDFLATEX           = YES
+LATEX_BATCHMODE        = NO
+LATEX_HIDE_INDICES     = NO
+LATEX_SOURCE_CODE      = NO
+LATEX_BIB_STYLE        = plain
+LATEX_TIMESTAMP        = NO
+#---------------------------------------------------------------------------
+# Configuration options related to the RTF output
+#---------------------------------------------------------------------------
+GENERATE_RTF           = NO
+RTF_OUTPUT             = rtf
+COMPACT_RTF            = NO
+RTF_HYPERLINKS         = NO
+RTF_STYLESHEET_FILE    =
+RTF_EXTENSIONS_FILE    =
+RTF_SOURCE_CODE        = NO
+#---------------------------------------------------------------------------
+# Configuration options related to the man page output
+#---------------------------------------------------------------------------
+GENERATE_MAN           = NO
+MAN_OUTPUT             = man
+MAN_EXTENSION          = .3
+MAN_SUBDIR             =
+MAN_LINKS              = NO
+#---------------------------------------------------------------------------
+# Configuration options related to the XML output
+#---------------------------------------------------------------------------
+GENERATE_XML           = NO
+XML_OUTPUT             = xml
+XML_PROGRAMLISTING     = YES
+#---------------------------------------------------------------------------
+# Configuration options related to the DOCBOOK output
+#---------------------------------------------------------------------------
+GENERATE_DOCBOOK       = NO
+DOCBOOK_OUTPUT         = docbook
+DOCBOOK_PROGRAMLISTING = NO
+#---------------------------------------------------------------------------
+# Configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+GENERATE_AUTOGEN_DEF   = NO
+#---------------------------------------------------------------------------
+# Configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+GENERATE_PERLMOD       = NO
+PERLMOD_LATEX          = NO
+PERLMOD_PRETTY         = YES
+PERLMOD_MAKEVAR_PREFIX =
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+ENABLE_PREPROCESSING   = YES
+MACRO_EXPANSION        = NO
+EXPAND_ONLY_PREDEF     = NO
+SEARCH_INCLUDES        = YES
+INCLUDE_PATH           =
+INCLUDE_FILE_PATTERNS  =
+PREDEFINED             =
+EXPAND_AS_DEFINED      =
+SKIP_FUNCTION_MACROS   = YES
+#---------------------------------------------------------------------------
+# Configuration options related to external references
+#---------------------------------------------------------------------------
+TAGFILES               =
+GENERATE_TAGFILE       =
+ALLEXTERNALS           = NO
+EXTERNAL_GROUPS        = YES
+EXTERNAL_PAGES         = YES
+PERL_PATH              = /usr/bin/perl
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+CLASS_DIAGRAMS         = NO
+MSCGEN_PATH            =
+DIA_PATH               =
+HIDE_UNDOC_RELATIONS   = YES
+HAVE_DOT               = YES
+DOT_NUM_THREADS        = 0
+DOT_FONTNAME           = Helvetica
+DOT_FONTSIZE           = 10
+DOT_FONTPATH           =
+CLASS_GRAPH            = YES
+COLLABORATION_GRAPH    = YES
+GROUP_GRAPHS           = YES
+UML_LOOK               = NO
+UML_LIMIT_NUM_FIELDS   = 10
+TEMPLATE_RELATIONS     = NO
+INCLUDE_GRAPH          = YES
+INCLUDED_BY_GRAPH      = YES
+CALL_GRAPH             = NO
+CALLER_GRAPH           = NO
+GRAPHICAL_HIERARCHY    = YES
+DIRECTORY_GRAPH        = YES
+DOT_IMAGE_FORMAT       = png
+INTERACTIVE_SVG        = NO
+DOT_PATH               =
+DOTFILE_DIRS           =
+MSCFILE_DIRS           =
+DIAFILE_DIRS           =
+PLANTUML_JAR_PATH      =
+PLANTUML_CFG_FILE      =
+PLANTUML_INCLUDE_PATH  =
+DOT_GRAPH_MAX_NODES    = 50
+MAX_DOT_GRAPH_DEPTH    = 0
+DOT_TRANSPARENT        = NO
+DOT_MULTI_TARGETS      = NO
+GENERATE_LEGEND        = YES
+DOT_CLEANUP            = YES
diff --git a/README b/README
index 0c82d67..803d502 100644
--- a/README
+++ b/README
@@ -79,12 +79,6 @@ FRACTION = 100000000
 # if not given.
 DATABASE = postgres:///talerlocal
 
-# See above
-UWSGI_SERVE = unix
-
-# See above
-UWSGI_UNIXPATH_MODE = 660
-
 # Maximum debt allowed for normal users.  The notation
 # used for amounts is: CURRENCY:x.y.  Note, at least one
 # digit in the fractional part is required.
diff --git a/configure.ac b/configure.ac
index f242d92..d70c52a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,6 +1,6 @@
 # This script is in the public domain.
 AC_PREREQ(2.61)
-AC_INIT([taler-bank], [0.5.0], address@hidden)
+AC_INIT([taler-bank], [0.5.1], address@hidden)
 
 AC_CONFIG_MACRO_DIR([m4])
 
diff --git a/requirements.txt b/requirements.txt
index fb116ed..05f7b3a 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,5 +1,4 @@
 Django==1.9.5
-django-simple-math-captcha==1.0.7
 psycopg2==2.6.1
 requests==2.9.1
 uWSGI==2.0.12
diff --git a/setup.py b/setup.py
index b2f2988..e9d1375 100755
--- a/setup.py
+++ b/setup.py
@@ -1,3 +1,5 @@
+# This file is in the public domain.
+
 from setuptools import setup, find_packages
 
 setup(name='talerbank',
@@ -13,20 +15,18 @@ setup(name='talerbank',
                         "requests",
                         "uWSGI",
                         "validictory",
-                        "mock"
+                        "mock",
+                        "jinja2"
                         ],
 
       package_data={
         'talerbank.app': [
             'templates/*.html',
             'templates/registration/*.html',
-            'static/web-common/*.js.tar.gz',
             'static/web-common/*.css',
             'static/web-common/*.html',
-            'static/web-common/*.js',
             'static/web-common/*.png',
             'static/*.css',
-            'static/*.js'
             ]
       },
       scripts=['taler-bank-manage'],
diff --git a/taler-bank-manage.in b/taler-bank-manage.in
index 935c8d5..82f4156 100644
--- a/taler-bank-manage.in
+++ b/taler-bank-manage.in
@@ -1,9 +1,25 @@
 #!/usr/bin/env python3
 
-"""
-Stand-alone script to manage
-the GNU Taler bank.
-"""
+##
+# This file is part of TALER
+# (C) 2017 INRIA
+#
+# TALER is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Affero General Public
+# License as published by the Free Software Foundation; either
+# version 3, or (at your option) any later version.
+#
+# TALER is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty
+# of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+# See the GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public
+# License along with TALER; see the file COPYING.  If not,
+# see <http://www.gnu.org/licenses/>
+#
+#  @author Florian Dold
+#  @file CLI tool to manage all the bank's tasks.
 
 import argparse
 import django
@@ -20,12 +36,20 @@ site.addsitedir("%s/lib/python%d.%d/site-packages" % (
     sys.version_info.major,
     sys.version_info.minor))
 
+## Not commenting global variables, at least for now.
+# @cond
 LOGGER = logging.getLogger(__name__)
-TC = TalerConfig.from_file(os.environ.get("TALER_CONFIG_FILE"))
 
 # No perfect match to our logging format, but good enough ...
 UWSGI_LOGFMT = "%(ltime) %(proto) %(method) %(uri) %(proto) => %(status)"
-
+## @endcond
+
+##
+# This function interprets the 'django' subcommand.
+# This command usually manages database tasks, and in
+# general what is not related to running the bank.
+#
+# @param args command line options.
 def handle_django(args):
     django.setup()
     # always run 'migrate' first, in case a virgin db is being used.
@@ -33,7 +57,11 @@ def handle_django(args):
     from django.core.management import execute_from_command_line
     execute_from_command_line([sys.argv[0] + " django"] + args.command)
 
-
+##
+# This function interprets the 'serve-http' subcommand.
+# The effect it to launch the bank HTTP service.
+#
+# @param args command line options.
 def handle_serve_http(args):
     import django
     django.setup()
@@ -41,6 +69,7 @@ def handle_serve_http(args):
     call_command('provide_accounts')
     call_command('check')
     port = args.port
+    TC = TalerConfig.from_file(os.environ.get("TALER_CONFIG_FILE", 
args.config))
     if port is None:
         port = TC["bank"]["http_port"].value_int(required=True)
 
@@ -53,13 +82,19 @@ def handle_serve_http(args):
               "--wsgi-file", "@prefix@/share/taler-bank/bank.wsgi"]
     os.execlp(*params)
 
-
+##
+# This function interprets the 'serve-uwsgi' subcommand.
+# The effect is to launch the bank UWSGI service.  This
+# type of service is usually used when the HTTP bank interface
+# is accessed via a reverse proxy (like Nginx, for example).
+#
+# @param command line options.
 def handle_serve_uwsgi(args):
-    del args # pacify PEP checkers
     django.setup()
     call_command('migrate')
     call_command('provide_accounts')
     call_command('check')
+    TC = TalerConfig.from_file(os.environ.get("TALER_CONFIG_FILE", 
args.config))
     serve_uwsgi = TC["bank"]["uwsgi_serve"].value_string(required=True).lower()
     params = ["uwsgi", "uwsgi",
               "--master",
@@ -79,15 +114,24 @@ def handle_serve_uwsgi(args):
     logging.info("launching uwsgi with argv %s", params[1:])
     os.execlp(*params)
 
+##
+# Currently deprecated.
 def handle_sampledata():
     django.setup()
     call_command('sample_donations')
 
+
+##
+# Interprets the '--config' option.
+#
+# @param args command line options.
 def handle_config(args):
     TC.from_file(args.config)
     TC.dump()
 
 
+## Not commenting global variables, at least for now.
+# @cond
 PARSER = argparse.ArgumentParser()
 PARSER.set_defaults(func=None)
 PARSER.add_argument('--config', '-c', help="configuration file to use",
@@ -132,3 +176,4 @@ try:
 except django.db.utils.OperationalError:
     LOGGER.error("Your database has serious problems. Does it exist?")
     sys.exit(4)
+## @endcond
diff --git a/talerbank/app/amount.py b/talerbank/app/amount.py
index e4c4555..83f91e0 100644
--- a/talerbank/app/amount.py
+++ b/talerbank/app/amount.py
@@ -1,5 +1,6 @@
-#  This file is part of TALER
-#  (C) 2017 TALER SYSTEMS
+##
+# This file is part of TALER
+# (C) 2017 TALER SYSTEMS
 #
 #  This library is free software; you can redistribute it and/or
 #  modify it under the terms of the GNU Lesser General Public
@@ -24,19 +25,41 @@
 #  mentioned above, and it is meant to be manually copied into
 #  any project which might need it.
 
+
+##
+# Exception class to raise when an operation between two
+# amounts of different currency is being attempted.
 class CurrencyMismatch(Exception):
-    hint = "Internal logic error (currency mismatch)"
-    http_status_code = 500
+    hint = "Client gave amount with unsupported currency."
+    http_status_code = 406
+
+    ##
+    # Init constructor.
+    #
+    # @param self the object itself.
+    # @param curr1 first currency involved in the operation.
+    # @param curr2 second currency involved in the operation.
     def __init__(self, curr1, curr2) -> None:
         super(CurrencyMismatch, self).__init__(
             "%s vs %s" % (curr1, curr2))
 
+##
+# Exception class to raise when a amount string is not valid.
 class BadFormatAmount(Exception):
+
     hint = "Malformed amount string"
+
+    ##
+    # Init constructor.
+    #
+    # @param self the object itself.
+    # @param faulty_str the invalid amount string.
     def __init__(self, faulty_str) -> None:
         super(BadFormatAmount, self).__init__(
             "Bad format amount: " + faulty_str)
 
+##
+# Main Amount class.
 class NumberTooBig(Exception):
     hint = "Number given is too big"
     def __init__(self) -> None:
@@ -50,16 +73,28 @@ class NegativeNumber(Exception):
             "Negative number given as value and/or fraction")
 
 class Amount:
+    ##
     # How many "fraction" units make one "value" unit of currency
     # (Taler requires 10^8).  Do not change this 'constant'.
     @staticmethod
     def _fraction() -> int:
         return 10 ** 8
 
+    ##
+    # Max value admitted: 2^53 - 1.  This constant is dictated
+    # by the wallet: JavaScript does not go beyond this value.
     @staticmethod
     def _max_value() -> int:
         return (2 ** 53) - 1
 
+
+    ##
+    # Init constructor.
+    #
+    # @param self the object itself.
+    # @param currency the amount's currency.
+    # @param value integer part the amount
+    # @param fraction fractional part of the amount
     def __init__(self, currency, value=0, fraction=0) -> None:
         if value < 0 or fraction < 0:
             raise NegativeNumber()
@@ -70,14 +105,21 @@ class Amount:
         if self.value > Amount._max_value():
             raise NumberTooBig()
 
-    # Normalize amount
+    ##
+    # Normalize amount.  It means it makes sure that the
+    # fractional part is less than one unit, and transfers
+    # the overhead to the integer part.
     def __normalize(self) -> None:
         if self.fraction >= Amount._fraction():
             self.value += int(self.fraction / Amount._fraction())
             self.fraction = self.fraction % Amount._fraction()
 
-    # Parse a string matching the format "A:B.C"
+    ##
+    # Parse a string matching the format "A:B.C",
     # instantiating an amount object.
+    #
+    # @param cls unused.
+    # @param amount_str the stringified amount to parse.
     @classmethod
     def parse(cls, amount_str: str):
         exp = r'^\s*([-_*A-Za-z0-9]+):([0-9]+)\.?([0-9]+)?\s*$'
@@ -85,16 +127,47 @@ class Amount:
         parsed = re.search(exp, amount_str)
         if not parsed:
             raise BadFormatAmount(amount_str)
+
+        ##
+        # Checks if the input overflows.
+        #
+        # @param arg the input number to check.
+        # @return True if the overflow occurs, False otherwise.
+        def check_overflow(arg):
+            # Comes from 2^53 - 1
+            JAVASCRIPT_MAX_INT = "9007199254740991"
+            if len(JAVASCRIPT_MAX_INT) < len(arg):
+                return True
+            if len(JAVASCRIPT_MAX_INT) == len(arg):
+                # Assume current system can afford to store
+                # a number as big as JAVASCRIPT_MAX_INT.
+                tmp = int(arg)
+                tmp_js = int(JAVASCRIPT_MAX_INT)
+                
+                if tmp > tmp_js - 1: # - 1 leaves room for the fractional part
+                    return True
+            return False
+
+        if check_overflow(parsed.group(2)):
+            raise AmountOverflow("integer part")
+
         value = int(parsed.group(2))
         fraction = 0
         for i, digit in enumerate(parsed.group(3) or "0"):
             fraction += int(int(digit) * (Amount._fraction() / 10 ** (i+1)))
+            if check_overflow(str(fraction)):
+                raise AmountOverflow("fraction")
+
         return cls(parsed.group(1), value, fraction)
 
-    # Comare two amounts, return:
-    # -1 if a < b
-    # 0 if a == b
-    # 1 if a > b
+    ##
+    # Compare two amounts.
+    #
+    # @param am1 first amount to compare.
+    # @param am2 second amount to compare.
+    # @return -1 if a < b
+    #          0 if a == b
+    #          1 if a > b
     @staticmethod
     def cmp(am1, am2) -> int:
         if am1.currency != am2.currency:
@@ -109,12 +182,24 @@ class Amount:
             return -1
         return 1
 
+
+    ##
+    # Setter method for the current object.
+    #
+    # @param self the object itself.
+    # @param currency the currency to set.
+    # @param value the value to set.
+    # @param fraction the fraction to set.
     def set(self, currency: str, value=0, fraction=0) -> None:
         self.currency = currency
         self.value = value
         self.fraction = fraction
 
-    # Add the given amount to this one
+    ##
+    # Add the given amount to this one.
+    #
+    # @param self the object itself.
+    # @param amount the amount to add to this one.
     def add(self, amount) -> None:
         if self.currency != amount.currency:
             raise CurrencyMismatch(self.currency, amount.currency)
@@ -122,7 +207,11 @@ class Amount:
         self.fraction += amount.fraction
         self.__normalize()
 
-    # Subtract passed amount from this one
+    ##
+    # Subtract amount from this one.
+    #
+    # @param self this object.
+    # @param amount the amount to subtract to this one.
     def subtract(self, amount) -> None:
         if self.currency != amount.currency:
             raise CurrencyMismatch(self.currency, amount.currency)
@@ -134,8 +223,14 @@ class Amount:
         self.value -= amount.value
         self.fraction -= amount.fraction
 
+    ##
     # Dump string from this amount, will put 'ndigits' numbers
     # after the dot.
+    #
+    # @param self this object.
+    # @param ndigits how many digits we want for the fractional part.
+    # @param pretty if True, put the currency in the last position and
+    #        omit the colon.
     def stringify(self, ndigits: int, pretty=False) -> str:
         if ndigits <= 0:
             raise BadFormatAmount("ndigits must be > 0")
@@ -149,7 +244,11 @@ class Amount:
             return "%s:%d.%s" % (self.currency, self.value, fraction_str)
         return "%d.%s %s" % (self.value, fraction_str, self.currency)
 
-    # Dump the Taler-compliant 'dict' amount
+    ##
+    # Dump the Taler-compliant 'dict' amount from
+    # this object.
+    #
+    # @param self this object.
     def dump(self) -> dict:
         return dict(value=self.value,
                     fraction=self.fraction,
diff --git a/talerbank/app/management/commands/dump_talerdb.py 
b/talerbank/app/management/commands/dump_talerdb.py
index 956c07e..ca9526c 100644
--- a/talerbank/app/management/commands/dump_talerdb.py
+++ b/talerbank/app/management/commands/dump_talerdb.py
@@ -1,12 +1,13 @@
-#  This file is part of TALER
-#  (C) 2014, 2015, 2106 INRIA
+##
+# This file is part of TALER
+# (C) 2014, 2015, 2106 INRIA
 #
-#  TALER is free software; you can redistribute it and/or modify
+# TALER is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published
 # by the Free Software Foundation; either version 3, or (at your
 # option) any later version.
 #
-#  TALER is distributed in the hope that it will be useful, but
+# TALER is distributed in the hope that it will be useful, but
 # WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 # General Public License for more details.
@@ -15,7 +16,8 @@
 # License along with TALER; see the file COPYING.  If not, see
 # <http://www.gnu.org/licenses/>
 #
-#  @author Marcello Stanisci
+# @author Marcello Stanisci
+# @brief dump database content in a pretty format.
 
 import sys
 import logging
@@ -25,6 +27,9 @@ from ...models import BankAccount, BankTransaction
 
 LOGGER = logging.getLogger(__name__)
 
+
+##
+# Dump bank accounts that exist in the database.
 def dump_accounts():
     try:
         accounts = BankAccount.objects.all()
@@ -40,6 +45,8 @@ def dump_accounts():
         sys.exit(1)
 
 
+##
+# Dump all the transactions that exist in the database.
 def dump_history():
     try:
         history = BankTransaction.objects.all()
@@ -55,7 +62,18 @@ def dump_history():
         sys.exit(1)
 
 
+##
+# Django-specific class that register this CLI utility.
 class Command(BaseCommand):
+
+    ##
+    # Django-specific callable that gets invoked when the user
+    # calls the CLI utility: simply, it calls the two helper
+    # functions defined above.
+    #
+    # @param self this object itself.
+    # @param args argument list (as passed by Django)
+    # @param options CLI options (also as passed by Django)
     def handle(self, *args, **options):
         dump_accounts()
         dump_history()
diff --git a/talerbank/app/management/commands/provide_accounts.py 
b/talerbank/app/management/commands/provide_accounts.py
index 6bf8c53..a7d9820 100644
--- a/talerbank/app/management/commands/provide_accounts.py
+++ b/talerbank/app/management/commands/provide_accounts.py
@@ -1,19 +1,24 @@
-#  This file is part of TALER
-#  (C) 2014, 2015, 2106 INRIA
+##
+# This file is part of TALER
+# (C) 2014, 2015, 2106 INRIA
 #
-#  TALER is free software; you can redistribute it and/or modify it under the
-#  terms of the GNU General Public License as published by the Free Software
-#  Foundation; either version 3, or (at your option) any later version.
+# TALER is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 3,
+# or (at your option) any later version.
 #
-#  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
-#  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 
FOR
-#  A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+# TALER is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
 #
-#  You should have received a copy of the GNU General Public License along with
-#  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+# You should have received a copy of the GNU General Public
+# License along with TALER; see the file COPYING.  If not,
+# see <http://www.gnu.org/licenses/>
 #
-#  @author Marcello Stanisci
-#  @author Florian Dold
+# @author Marcello Stanisci
+# @author Florian Dold
+# @brief Create the basic accounts to make the demo bank work.
 
 import sys
 import logging
@@ -22,11 +27,16 @@ from django.db.utils import ProgrammingError, 
OperationalError
 from django.core.management.base import BaseCommand
 from django.conf import settings
 from ...models import BankAccount
+from ...views import wire_transfer
+from ...amount import Amount
 
 LOGGER = logging.getLogger(__name__)
 LOGGER.setLevel(logging.INFO)
 
-
+##
+# Create a new bank account.
+# 
+# @param username the username to associate to this account.
 def make_account(username):
     try:
         User.objects.get(username=username)
@@ -37,6 +47,15 @@ def make_account(username):
                 username=username, password='x'),
             is_public=True).save()
 
+        if "Survey" == username:
+            bank = BankAccount.objects.get(account_no=1)
+            survey = BankAccount.objects.get(account_no=8)
+            wire_transfer(Amount(settings.TALER_CURRENCY,
+                                  10000000),
+                          bank,
+                          survey,
+                          "Benevolent donation for 'Survey'") 
+
     except (OperationalError, ProgrammingError):
         LOGGER.error("db does not exist, or the project"
                      " is not migrated.  Try 'taler-bank-manage"
@@ -45,9 +64,16 @@ def make_account(username):
                      exc_info=True)
         sys.exit(1)
 
+##
+# Django-specific definition to register this command.
 class Command(BaseCommand):
     help = "Provide initial user accounts"
 
+    ##
+    # Django-specific definition to invoke the account creator
+    # @a make_account; it iterates over the list of basic accounts
+    # (defined in the settings) and invoke the account creator
+    # for each one of them.
     def handle(self, *args, **options):
         for username in settings.TALER_PREDEFINED_ACCOUNTS:
             make_account(username)
diff --git a/talerbank/app/management/commands/wire_transfer.py 
b/talerbank/app/management/commands/wire_transfer.py
index 82f18d0..b0e006a 100644
--- a/talerbank/app/management/commands/wire_transfer.py
+++ b/talerbank/app/management/commands/wire_transfer.py
@@ -1,21 +1,23 @@
-#  This file is part of TALER
-#  (C) 2014, 2015, 2106 INRIA
+##
+# This file is part of TALER
+# (C) 2014, 2015, 2106 INRIA
 #
-#  TALER is free software; you can redistribute it and/or modify
+# TALER is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published
 # by the Free Software Foundation; either version 3, or (at your
 # option) any later version.
 #
-#  TALER is distributed in the hope that it will be useful, but
+# TALER is distributed in the hope that it will be useful, but
 # WITHOUT ANY WARRANTY; without even the implied warranty of
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 # General Public License for more details.
 #
-#  You should have received a copy of the GNU General Public
+# You should have received a copy of the GNU General Public
 # License along with TALER; see the file COPYING.  If not, see
 # <http://www.gnu.org/licenses/>
 #
-#  @author Marcello Stanisci
+# @author Marcello Stanisci
+# @brief CLI utility that issues a wire transfer.
 
 import sys
 import logging
@@ -28,9 +30,18 @@ from ...models import BankAccount, BankTransaction
 
 LOGGER = logging.getLogger(__name__)
 
+
+
+##
+# Django-specific definition to register the CLI utility.
 class Command(BaseCommand):
     help = "Wire transfer money and return the transaction id."
 
+    ##
+    # Register the command line options of this command.
+    #
+    # @param self this object.
+    # @param parser API used to actually register the option.
     def add_arguments(self, parser):
         parser.add_argument(
             "user", type=str, metavar="USERNAME",
@@ -50,8 +61,15 @@ class Command(BaseCommand):
             "CURRENCY:X.Y form.")
 
 
+    ##
+    # This callable gets invoked when the user invokes the
+    # CLI utility; it is responsible of making the wire transfer
+    # effective.
+    #
+    # @param self this object.
+    # @param args arguments list -- currently unused.
+    # @param options options given by the user at the command line.
     def handle(self, *args, **options):
-
         user = authenticate(
             username=options["user"], password=options["password"])
         if not user:
diff --git a/talerbank/app/middleware.py b/talerbank/app/middleware.py
index 945e888..a5888e2 100644
--- a/talerbank/app/middleware.py
+++ b/talerbank/app/middleware.py
@@ -14,11 +14,24 @@ from .amount import \
 
 LOGGER = logging.getLogger()
 
+
+##
+# Class holding data needed by the handling logic.
 class ExceptionMiddleware:
 
+    ##
+    # Init constructor.
+    #
+    # @param self the object itself.
+    # @param get_response a Django-provided callable that calls
+    #        whatever comes next in the chain: a further middleware
+    #        or the view itself (please refer to the official
+    #        documentation for more details).
     def __init__(self, get_response):
         self.get_response = get_response
 
+        # List of all the exceptions that are managed by
+        # this module.
         self.excs = {
             BankAccount.DoesNotExist: 0,
             BankTransaction.DoesNotExist: 1,
@@ -35,12 +48,17 @@ class ExceptionMiddleware:
             NumberTooBig: 1,
             NegativeNumber: 0}
 
+        # List of all the HTTP endpoint that are likely
+        # to generate exceptions.
         self.apis = {
             "/withdraw": 5400,
             "/reject": 5300,
             "/history": 5200,
             "/admin/add/incoming": 5100}
 
+
+        # Map between endpoints and Web pages to render
+        # after the exception gets managed.
         self.render = {
             "/profile": "profile",
             "/register": "index",
@@ -48,17 +66,46 @@ class ExceptionMiddleware:
             "/pin/verify": "profile",
             "/withdraw": "profile"}
 
-
+    ##
+    # This function is transparently invoked by Django when
+    # a request traverses the chain made of middleware classes
+    # and the view itself as the last element in the chain.
+    #
+    # @param self this class.
+    # @param request Django-specific request object (of the same
+    #        type that is handed to views).
+    # @return Django-specific response object.
     def __call__(self, request):
         return self.get_response(request)
 
+    ##
+    # Main logic for processing the exception.  It checks
+    # if the exception captured can be managed, and does it
+    # if so.  Otherwise, it lets the native handler operate.
+    #
+    # @param self a @a ExceptionMiddleware object.
+    # @param request Django-specific HTTP request.
+    # @param exception the exception raised from the bank.
     def process_exception(self, request, exception):
-        if not self.excs.get(exception.__class__):
+
+        # See if we manage this exception.  Return None if not.
+        exc_class = None
+        for e in self.excs:
+            if isinstance(exception, e):
+                exc_class = e
+                break
+        if not exc_class:
             return None
-        taler_ec = self.excs.get(exception.__class__)
-        # The way error codes compose matches definitions found
-        # at [1].
+        
+        # Managed exception.  Build response.
+        taler_ec = self.excs.get(exc_class)
+
+        # The way error codes compose matches
+        # definitions found in [1].
         taler_ec += self.apis.get(request.path, 1000)
+
+        # Check if the endpoint should cause a human-readable
+        # page to be returned.
         render_to = self.render.get(request.path)
         if not render_to:
             return JsonResponse({"ec": taler_ec,
diff --git a/talerbank/app/migrations/0001_initial.py 
b/talerbank/app/migrations/0001_squashed_0013_remove_banktransaction_reimburses.py
similarity index 52%
rename from talerbank/app/migrations/0001_initial.py
rename to 
talerbank/app/migrations/0001_squashed_0013_remove_banktransaction_reimburses.py
index ff05150..31c38a3 100644
--- a/talerbank/app/migrations/0001_initial.py
+++ 
b/talerbank/app/migrations/0001_squashed_0013_remove_banktransaction_reimburses.py
@@ -1,14 +1,15 @@
-# -*- coding: utf-8 -*-
-# Generated by Django 1.10.3 on 2017-03-22 20:22
-from __future__ import unicode_literals
+# Generated by Django 2.0.1 on 2018-02-13 10:23
 
 from django.conf import settings
 from django.db import migrations, models
 import django.db.models.deletion
+import talerbank.app.models
 
 
 class Migration(migrations.Migration):
 
+    replaces = [('app', '0001_initial'), ('app', '0002_bankaccount_amount'), 
('app', '0003_auto_20171030_1346'), ('app', '0004_auto_20171030_1428'), ('app', 
'0005_remove_banktransaction_currency'), ('app', '0006_auto_20171031_0823'), 
('app', '0007_auto_20171031_0906'), ('app', '0008_auto_20171031_0938'), ('app', 
'0009_auto_20171120_1642'), ('app', '0010_banktransaction_cancelled'), ('app', 
'0011_banktransaction_reimburses'), ('app', '0012_auto_20171212_1540'), ('app', 
'0013_remove_banktr [...]
+
     initial = True
 
     dependencies = [
@@ -33,13 +34,33 @@ class Migration(migrations.Migration):
             name='BankTransaction',
             fields=[
                 ('id', models.AutoField(auto_created=True, primary_key=True, 
serialize=False, verbose_name='ID')),
-                ('amount_value', models.IntegerField(default=0)),
-                ('amount_fraction', models.IntegerField(default=0)),
-                ('currency', models.CharField(max_length=12)),
                 ('subject', models.CharField(default='(no subject given)', 
max_length=200)),
-                ('date', models.DateTimeField(auto_now=True)),
+                ('date', models.DateTimeField(auto_now=True, db_index=True)),
                 ('credit_account', 
models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, 
related_name='credit_account', to='app.BankAccount')),
                 ('debit_account', 
models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, 
related_name='debit_account', to='app.BankAccount')),
+                ('amount', talerbank.app.models.AmountField(default=False)),
+                ('cancelled', models.BooleanField(default=False)),
             ],
         ),
+        migrations.AddField(
+            model_name='bankaccount',
+            name='amount',
+            
field=talerbank.app.models.AmountField(default=talerbank.app.models.get_zero_amount),
+        ),
+        migrations.RemoveField(
+            model_name='bankaccount',
+            name='balance',
+        ),
+        migrations.RemoveField(
+            model_name='bankaccount',
+            name='balance_fraction',
+        ),
+        migrations.RemoveField(
+            model_name='bankaccount',
+            name='balance_value',
+        ),
+        migrations.RemoveField(
+            model_name='bankaccount',
+            name='currency',
+        ),
     ]
diff --git a/talerbank/app/migrations/0002_bankaccount_amount.py 
b/talerbank/app/migrations/0002_bankaccount_amount.py
deleted file mode 100644
index beaa1d8..0000000
--- a/talerbank/app/migrations/0002_bankaccount_amount.py
+++ /dev/null
@@ -1,21 +0,0 @@
-# -*- coding: utf-8 -*-
-# Generated by Django 1.10.3 on 2017-10-30 13:23
-from __future__ import unicode_literals
-
-from django.db import migrations
-import talerbank.app.models
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('app', '0001_initial'),
-    ]
-
-    operations = [
-        migrations.AddField(
-            model_name='bankaccount',
-            name='amount',
-            field=talerbank.app.models.AmountField(default=False),
-        ),
-    ]
diff --git a/talerbank/app/migrations/0003_auto_20171030_1346.py 
b/talerbank/app/migrations/0003_auto_20171030_1346.py
deleted file mode 100644
index 91c6cb9..0000000
--- a/talerbank/app/migrations/0003_auto_20171030_1346.py
+++ /dev/null
@@ -1,21 +0,0 @@
-# -*- coding: utf-8 -*-
-# Generated by Django 1.10.3 on 2017-10-30 13:46
-from __future__ import unicode_literals
-
-from django.db import migrations
-import talerbank.app.models
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('app', '0002_bankaccount_amount'),
-    ]
-
-    operations = [
-        migrations.AlterField(
-            model_name='bankaccount',
-            name='amount',
-            field=talerbank.app.models.AmountField(),
-        ),
-    ]
diff --git a/talerbank/app/migrations/0004_auto_20171030_1428.py 
b/talerbank/app/migrations/0004_auto_20171030_1428.py
deleted file mode 100644
index b93ebd4..0000000
--- a/talerbank/app/migrations/0004_auto_20171030_1428.py
+++ /dev/null
@@ -1,34 +0,0 @@
-# -*- coding: utf-8 -*-
-# Generated by Django 1.10.3 on 2017-10-30 14:28
-from __future__ import unicode_literals
-
-from django.db import migrations
-import talerbank.app.models
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('app', '0003_auto_20171030_1346'),
-    ]
-
-    operations = [
-        migrations.RemoveField(
-            model_name='banktransaction',
-            name='amount_fraction',
-        ),
-        migrations.RemoveField(
-            model_name='banktransaction',
-            name='amount_value',
-        ),
-        migrations.AddField(
-            model_name='banktransaction',
-            name='amount',
-            field=talerbank.app.models.AmountField(default=False),
-        ),
-        migrations.AlterField(
-            model_name='bankaccount',
-            name='amount',
-            field=talerbank.app.models.AmountField(default=False),
-        ),
-    ]
diff --git a/talerbank/app/migrations/0005_remove_banktransaction_currency.py 
b/talerbank/app/migrations/0005_remove_banktransaction_currency.py
deleted file mode 100644
index 9cd781f..0000000
--- a/talerbank/app/migrations/0005_remove_banktransaction_currency.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# -*- coding: utf-8 -*-
-# Generated by Django 1.10.3 on 2017-10-30 14:37
-from __future__ import unicode_literals
-
-from django.db import migrations
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('app', '0004_auto_20171030_1428'),
-    ]
-
-    operations = [
-        migrations.RemoveField(
-            model_name='banktransaction',
-            name='currency',
-        ),
-    ]
diff --git a/talerbank/app/migrations/0006_auto_20171031_0823.py 
b/talerbank/app/migrations/0006_auto_20171031_0823.py
deleted file mode 100644
index 67c1a70..0000000
--- a/talerbank/app/migrations/0006_auto_20171031_0823.py
+++ /dev/null
@@ -1,21 +0,0 @@
-# -*- coding: utf-8 -*-
-# Generated by Django 1.10.3 on 2017-10-31 08:23
-from __future__ import unicode_literals
-
-from django.db import migrations
-import talerbank.app.models
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('app', '0005_remove_banktransaction_currency'),
-    ]
-
-    operations = [
-        migrations.AlterField(
-            model_name='bankaccount',
-            name='amount',
-            field=talerbank.app.models.AmountField(default=None),
-        ),
-    ]
diff --git a/talerbank/app/migrations/0007_auto_20171031_0906.py 
b/talerbank/app/migrations/0007_auto_20171031_0906.py
deleted file mode 100644
index 923cff2..0000000
--- a/talerbank/app/migrations/0007_auto_20171031_0906.py
+++ /dev/null
@@ -1,21 +0,0 @@
-# -*- coding: utf-8 -*-
-# Generated by Django 1.10.3 on 2017-10-31 09:06
-from __future__ import unicode_literals
-
-from django.db import migrations
-import talerbank.app.models
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('app', '0006_auto_20171031_0823'),
-    ]
-
-    operations = [
-        migrations.AlterField(
-            model_name='bankaccount',
-            name='amount',
-            
field=talerbank.app.models.AmountField(default=talerbank.app.models.get_zero_amount),
-        ),
-    ]
diff --git a/talerbank/app/migrations/0008_auto_20171031_0938.py 
b/talerbank/app/migrations/0008_auto_20171031_0938.py
deleted file mode 100644
index 3b97829..0000000
--- a/talerbank/app/migrations/0008_auto_20171031_0938.py
+++ /dev/null
@@ -1,31 +0,0 @@
-# -*- coding: utf-8 -*-
-# Generated by Django 1.10.3 on 2017-10-31 09:38
-from __future__ import unicode_literals
-
-from django.db import migrations
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('app', '0007_auto_20171031_0906'),
-    ]
-
-    operations = [
-        migrations.RemoveField(
-            model_name='bankaccount',
-            name='balance',
-        ),
-        migrations.RemoveField(
-            model_name='bankaccount',
-            name='balance_fraction',
-        ),
-        migrations.RemoveField(
-            model_name='bankaccount',
-            name='balance_value',
-        ),
-        migrations.RemoveField(
-            model_name='bankaccount',
-            name='currency',
-        ),
-    ]
diff --git a/talerbank/app/migrations/0009_auto_20171120_1642.py 
b/talerbank/app/migrations/0009_auto_20171120_1642.py
deleted file mode 100644
index 590fb93..0000000
--- a/talerbank/app/migrations/0009_auto_20171120_1642.py
+++ /dev/null
@@ -1,20 +0,0 @@
-# -*- coding: utf-8 -*-
-# Generated by Django 1.10.3 on 2017-11-20 16:42
-from __future__ import unicode_literals
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('app', '0008_auto_20171031_0938'),
-    ]
-
-    operations = [
-        migrations.AlterField(
-            model_name='banktransaction',
-            name='date',
-            field=models.DateTimeField(auto_now=True, db_index=True),
-        ),
-    ]
diff --git a/talerbank/app/migrations/0010_banktransaction_cancelled.py 
b/talerbank/app/migrations/0010_banktransaction_cancelled.py
deleted file mode 100644
index ee46222..0000000
--- a/talerbank/app/migrations/0010_banktransaction_cancelled.py
+++ /dev/null
@@ -1,20 +0,0 @@
-# -*- coding: utf-8 -*-
-# Generated by Django 1.10.3 on 2017-12-07 16:11
-from __future__ import unicode_literals
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('app', '0009_auto_20171120_1642'),
-    ]
-
-    operations = [
-        migrations.AddField(
-            model_name='banktransaction',
-            name='cancelled',
-            field=models.BooleanField(default=False),
-        ),
-    ]
diff --git a/talerbank/app/migrations/0011_banktransaction_reimburses.py 
b/talerbank/app/migrations/0011_banktransaction_reimburses.py
deleted file mode 100644
index 6ea385d..0000000
--- a/talerbank/app/migrations/0011_banktransaction_reimburses.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# Generated by Django 2.0 on 2017-12-12 15:31
-
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('app', '0010_banktransaction_cancelled'),
-    ]
-
-    operations = [
-        migrations.AddField(
-            model_name='banktransaction',
-            name='reimburses',
-            field=models.ForeignKey(default=False, 
on_delete=django.db.models.deletion.CASCADE, related_name='reimburser', 
to='app.BankTransaction'),
-        ),
-    ]
diff --git a/talerbank/app/migrations/0012_auto_20171212_1540.py 
b/talerbank/app/migrations/0012_auto_20171212_1540.py
deleted file mode 100644
index 21af37a..0000000
--- a/talerbank/app/migrations/0012_auto_20171212_1540.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# Generated by Django 2.0 on 2017-12-12 15:40
-
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('app', '0011_banktransaction_reimburses'),
-    ]
-
-    operations = [
-        migrations.AlterField(
-            model_name='banktransaction',
-            name='reimburses',
-            field=models.ForeignKey(default=None, null=True, 
on_delete=django.db.models.deletion.CASCADE, related_name='reimburser', 
to='app.BankTransaction'),
-        ),
-    ]
diff --git a/talerbank/app/migrations/0013_remove_banktransaction_reimburses.py 
b/talerbank/app/migrations/0013_remove_banktransaction_reimburses.py
deleted file mode 100644
index 5b88248..0000000
--- a/talerbank/app/migrations/0013_remove_banktransaction_reimburses.py
+++ /dev/null
@@ -1,17 +0,0 @@
-# Generated by Django 2.0 on 2017-12-14 10:19
-
-from django.db import migrations
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('app', '0012_auto_20171212_1540'),
-    ]
-
-    operations = [
-        migrations.RemoveField(
-            model_name='banktransaction',
-            name='reimburses',
-        ),
-    ]
diff --git a/talerbank/app/models.py b/talerbank/app/models.py
index 03b9aba..5b52883 100644
--- a/talerbank/app/models.py
+++ b/talerbank/app/models.py
@@ -1,4 +1,5 @@
-#  This file is part of TALER
+##
+# This file is part of TALER
 #  (C) 2014, 2015, 2016 INRIA
 #
 #  TALER is free software; you can redistribute it and/or modify
@@ -38,22 +39,47 @@ class InvalidAmount(Amount):
 
 class AmountField(models.Field):
 
-    description = 'Amount object in Taler style'
+##
+# Helper function that instantiates a zero-valued @a Amount
+# object.
+def get_zero_amount() -> Amount:
+    return Amount(settings.TALER_CURRENCY)
 
-    def deconstruct(self) -> Tuple[str, str, list, dict]:
-        name, path, args, kwargs = super(
-            AmountField, self).deconstruct()
-        return name, path, args, kwargs
 
+##
+# Custom implementation of the @a Amount class as a database type.
+class AmountField(models.Field):
+    description = 'Amount object in Taler style'
+
+    ##
+    # Return the database type of the serialized amount.
+    #
+    # @param self the object itself.
+    # @param connection the database connection.
+    # @return type of the serialized amount: varchar.
     def db_type(self, connection: Any) -> str:
         return "varchar"
 
-    # Pass stringified object to db connector
+    ##
+    # Stringifies the Amount object to feed the DB connector.
+    #
+    # @param self the object itself.
+    # @para value the @a Amount object to be serialized.
     def get_prep_value(self, value: Amount) -> str:
         if not value:
             return "%s:0.0" % settings.TALER_CURRENCY
+        if settings.TALER_CURRENCY != value.currency:
+            raise CurrencyMismatch(settings.TALER_CURRENCY,
+                                   value.currency)
         return value.stringify(settings.TALER_DIGITS)
 
+    ##
+    # Parse the stringified Amount back to Python.
+    #
+    # @param value serialized amount coming from the database.
+    #        (It is just a string in the usual CURRENCY:X.Y form)
+    # @param args currently unused.
+    # @return the @a Amount object.
     @staticmethod
     def from_db_value(value: str, *args) -> Amount:
         del args # pacify PEP checkers
@@ -76,6 +102,16 @@ class AmountField(models.Field):
             # just try-cactch any possible exception situation.
             return InvalidAmount(settings.TALER_CURRENCY)
 
+
+    ##
+    # Parse the stringified Amount back to Python. FIXME:
+    # why this serializer consider _more_ cases respect to the
+    # one above ('from_db_value')?
+    #
+    # @param value serialized amount coming from the database.
+    #        (It is just a string in the usual CURRENCY:X.Y form)
+    # @param args currently unused.
+    # @return the @a Amount object.
     def to_python(self, value: Any) -> Amount:
         if isinstance(value, Amount):
             return value
@@ -87,17 +123,26 @@ class AmountField(models.Field):
             raise ValidationError(
                 "Invalid input for an amount string: %s" % value)
 
-def get_zero_amount() -> Amount:
-    return Amount(settings.TALER_CURRENCY)
 
+##
+# Exception class to raise when a non-existent bank account is
+# tried to be referenced.
 class BankAccountDoesNotExist(ObjectDoesNotExist):
     hint = "Specified bank account does not exist"
     http_status_code = 404
 
+
+##
+# Exception class to raise when a non-existent bank transaction is
+# tried to be referenced.
 class BankTransactionDoesNotExist(ObjectDoesNotExist):
     hint = "Specified bank transaction does not exist"
     http_status_code = 404
 
+
+
+##
+# The class representing a bank account.
 class BankAccount(models.Model):
     is_public = models.BooleanField(default=False)
     debit = models.BooleanField(default=False)
@@ -106,6 +151,9 @@ class BankAccount(models.Model):
     amount = AmountField(default=get_zero_amount)
     DoesNotExist = BankAccountDoesNotExist
 
+
+##
+# The class representing a bank transaction.
 class BankTransaction(models.Model):
     amount = AmountField(default=False)
     debit_account = models.ForeignKey(
diff --git a/talerbank/app/schemas.py b/talerbank/app/schemas.py
index 1ddb684..b268275 100644
--- a/talerbank/app/schemas.py
+++ b/talerbank/app/schemas.py
@@ -1,5 +1,6 @@
-#  This file is part of TALER
-#  (C) 2014, 2015, 2016 INRIA
+##
+# This file is part of TALER
+# (C) 2014, 2015, 2016 INRIA
 #
 #  TALER is free software; you can redistribute it and/or modify
 # it under the terms of the GNU Affero General Public License as
@@ -16,11 +17,7 @@
 # <http://www.gnu.org/licenses/>
 #
 #  @author Marcello Stanisci
-
-
-"""
-definitions of JSON schemas for validating data
-"""
+#  @brief definitions of JSON schemas for validating data
 
 import json
 from validictory import validate
@@ -30,45 +27,68 @@ from validictory.validator import \
 
 from django.conf import settings
 
-class UnknownCurrencyException(ValueError):
-    def __init__(self, hint, http_status_code):
-        self.hint = hint
-        self.http_status_code = http_status_code
-        super().__init__()
 
+##
+# Exception class to be raised when a expected URL parameter
+# is not found.
 class URLParameterMissing(ValueError):
+
+    ##
+    # Init method.
+    #
+    # @param self the object itself.
+    # @param param the missing URL parameter name.
+    # @param http_status_code the HTTP response code to return
+    #        to the caller (client).
     def __init__(self, param, http_status_code):
         self.hint = "URL parameter '%s' is missing" % param
         self.http_status_code = http_status_code
         super().__init__()
 
+##
+# Exception class to be raised when a expected URL parameter
+# is malformed.
 class URLParameterMalformed(ValueError):
+
+    ##
+    # Init method.
+    #
+    # @param self the object itself.
+    # @param param the malformed URL parameter name.
+    # @param http_status_code the HTTP response code to return
+    #        to the caller (client).
     def __init__(self, param, http_status_code):
         self.hint = "URL parameter '%s' is malformed" % param
         self.http_status_code = http_status_code
         super().__init__()
 
+
+##
+# Exception class to be raised when a JSON
+# object does not respect a specification.
 class JSONFieldException(ValueError):
+
+    ##
+    # Init method.
+    #
+    # @param self the object itself.
+    # @param hint the hint to be displayed along the error.
+    # @param http_status_code HTTP response code to be returned
+    #        along the error.
     def __init__(self, hint, http_status_code):
         self.hint = hint
         self.http_status_code = http_status_code
         super().__init__()
 
-'''
-AMOUNT_SCHEMA = {
-    "type": "object",
-    "properties": {
-        "value": {"type": "integer"},
-        "fraction": {"type": "integer"},
-        "currency": {"type": "string",
-                     "pattern": "^"+settings.TALER_CURRENCY+"$"}
-    }
-}'''
-
+##
+# Pattern for amounts.
 AMOUNT_SCHEMA = {
     "type": "string",
-    "pattern": "^"+settings.TALER_CURRENCY+":([0-9]+)\.?([0-9]+)?$"}
+    "pattern": "^[A-Za-z0-9_-]+:([0-9]+)\.?([0-9]+)?$"}
 
+
+##
+# Definition that withdraw request bodies have to match.
 WITHDRAW_SESSION_SCHEMA = {
     "type": "object",
     "properties": {
@@ -86,6 +106,8 @@ WITHDRAW_SESSION_SCHEMA = {
     }
 }
 
+##
+# Definition for wire details.
 WIREDETAILS_SCHEMA = {
     "type": "object",
     "properties": {
@@ -101,6 +123,8 @@ WIREDETAILS_SCHEMA = {
     }
 }
 
+##
+# Definition for authentication objects.
 AUTH_SCHEMA = {
     "type": "object",
     "properties": {
@@ -110,6 +134,9 @@ AUTH_SCHEMA = {
     }
 }
 
+
+##
+# Definition for reject request bodies.
 REJECT_REQUEST_SCHEMA = {
     "type": "object",
     "properties": {
@@ -119,6 +146,9 @@ REJECT_REQUEST_SCHEMA = {
     }
 }
 
+
+##
+# Definition for /history request URL parameters.
 HISTORY_REQUEST_SCHEMA = {
     "type": "object",
     "properties": {
@@ -131,6 +161,9 @@ HISTORY_REQUEST_SCHEMA = {
         "start": {"type": "string",
                   "pattern": "^([0-9]+)$",
                   "required": False},
+        "ordering": {"type": "string",
+                     "pattern": r"^(ascending|descending)$",
+                     "required": False},
         "direction": {"type": "string",
                       "pattern": r"^(debit|credit|both|cancel\+|cancel-)$"},
         "account_number": {"type": "string",
@@ -139,6 +172,9 @@ HISTORY_REQUEST_SCHEMA = {
     }
 }
 
+
+##
+# Definition for /add/incoming request bodies.
 INCOMING_REQUEST_SCHEMA = {
     "type": "object",
     "properties": {
@@ -149,6 +185,9 @@ INCOMING_REQUEST_SCHEMA = {
     }
 }
 
+
+##
+# Definition for PIN/TAN request URL parameters.
 PIN_TAN_ARGS = {
     "type": "object",
     "properties": {
@@ -161,6 +200,19 @@ PIN_TAN_ARGS = {
     }
 }
 
+##
+# Check the custom types for a PIN/TAN request.  Those
+# types the strings that must represent integers (like
+# those used for amount's values and fractions), and the
+# stringification of a wire details object.  All of them
+# get passed along the GET request's arguments by the
+# wallet.
+#
+# @param validator unused.
+# @param fieldname current name of the field being processed.
+# @param value current name of the value being processed.
+# @param format_option holds the format definition for the
+#        current field being processed.
 def validate_pintan_types(validator, fieldname, value, format_option):
     del validator # pacify PEP checkers
     try:
@@ -173,27 +225,65 @@ def validate_pintan_types(validator, fieldname, value, 
format_option):
         raise FieldValidationError(
             "Malformed '%s'" % fieldname, fieldname, value)
 
+##
+# Check the GET arguments given along a /pin/question request.
+#
+# @param data GET arguments in a dict.
 def validate_pin_tan(data):
     format_dict = {
         "str_to_int": validate_pintan_types,
         "wiredetails_string": validate_pintan_types}
     validate(data, PIN_TAN_ARGS, format_validators=format_dict)
 
+##
+# Check if the /reject request is valid.
+#
+# @param data POST/PUT body.
 def validate_reject(data):
     validate(data, REJECT_REQUEST_SCHEMA)
 
+##
+# Check /history input data.
+#
+# @param data dict representing /history's GET parameters.
 def validate_history(data):
     validate(data, HISTORY_REQUEST_SCHEMA)
 
+##
+# Check wire details
+# (regardless of which endpoint triggered the check)
+#
+# @param wiredetails object representing wire details.
 def validate_wiredetails(wiredetails):
     validate(wiredetails, WIREDETAILS_SCHEMA)
 
+##
+# Check input data for a wire transfer commanded via the
+# HTTP REST service.
+#
+# @param data POST body sent along the request.
 def validate_add_incoming(data):
     validate(data, INCOMING_REQUEST_SCHEMA)
 
+##
+# Check that the state corresponds to a withdrawal session.
+#
+# @param data the dict representing the server state.  So not
+#        strictly an 'input' sent by any client; we use this just
+#        as a double-checking mechanism to see if the server is
+#        well-behaved.
 def check_withdraw_session(data):
     validate(data, WITHDRAW_SESSION_SCHEMA)
 
+
+##
+# Abstraction over the real validators.  Do not return,
+# but rather raise exceptions if the data is invalid.
+#
+# @param request Django-specific HTTP request object.
+# @param data data to validate.  May be a POST body or
+#        a dict holding the param-value pairs from a GET
+#        request.
 def validate_data(request, data):
     switch = {
         "/reject": validate_reject,
@@ -202,6 +292,7 @@ def validate_data(request, data):
         "/pin/verify": check_withdraw_session,
         "/pin/question": validate_pin_tan
     }
+
     try:
         switch.get(request.path_info)(data)
     except  RequiredFieldValidationError as exc:
@@ -210,8 +301,6 @@ def validate_data(request, data):
         raise JSONFieldException(
             "Field '%s' is missing" % exc.fieldname, 400)
     except FieldValidationError as exc:
-        if exc.fieldname == "currency":
-            raise UnknownCurrencyException("Unknown currency", 406)
         if request.method == "GET":
             raise URLParameterMalformed(exc.fieldname, 400)
         raise JSONFieldException(
diff --git a/talerbank/app/static/web-common b/talerbank/app/static/web-common
index 489a9e3..e9554ba 160000
--- a/talerbank/app/static/web-common
+++ b/talerbank/app/static/web-common
@@ -1 +1 @@
-Subproject commit 489a9e38e6960fdce309ab8cf5ff66d930ecb3bc
+Subproject commit e9554baf0f3f880d656284ef5e9089bbd8313464
diff --git a/talerbank/app/templates/error_exchange.html 
b/talerbank/app/templates/404.html
similarity index 85%
copy from talerbank/app/templates/error_exchange.html
copy to talerbank/app/templates/404.html
index e3319a6..305487c 100644
--- a/talerbank/app/templates/error_exchange.html
+++ b/talerbank/app/templates/404.html
@@ -27,16 +27,7 @@
   </aside>
   <section id="main">
     <article>
-      <p>
-      {{ message }}
-      </p>
-      <p>
-      Status: {{ response_status }}
-      </p>
-      <p>
-      Response body:
-      <pre>{{ response_text }}</pre>
-      </p>
+      <p>Page not found!</p>
     </article>
   </section>
 {% endblock content %}
diff --git a/talerbank/app/templates/base.html 
b/talerbank/app/templates/base.html
index fe5fdcd..b537b0b 100644
--- a/talerbank/app/templates/base.html
+++ b/talerbank/app/templates/base.html
@@ -40,6 +40,7 @@
         <li><a href="{{ env('TALER_ENV_URL_MERCHANT_BLOG', '#') }}">Essay 
Shop</a></li>
         <li><a href="{{ env('TALER_ENV_URL_MERCHANT_DONATIONS', '#') 
}}">Donations</a></li>
         <li><a href="{{ env('TALER_ENV_URL_MERCHANT_SURVEY', '#') 
}}">Tipping/Survey</a></li>
+        <li><a href="{{ env('TALER_ENV_URL_BACKOFFICE', '#') 
}}">Back-office</a></li>
       </ul>
       <p>You can learn more about Taler on our main <a 
href="https://taler.net";>website</a>.</p>
     </div>
diff --git a/talerbank/app/templates/login.html 
b/talerbank/app/templates/login.html
index 40a5a5b..4e0d7eb 100644
--- a/talerbank/app/templates/login.html
+++ b/talerbank/app/templates/login.html
@@ -27,25 +27,6 @@
     <article>
       <div class="login-form">
         <h2>Please login!</h2>
-        {% if fail_message %}
-        <p class="informational informational-fail">
-          {{ hint }}
-        </p>
-        {% endif %}
-        {% if success_message %}
-        <p class="informational informational-ok">
-          {{ hint }}
-        </p>
-        {% endif %}
-
-        {% if next %}
-            {% if user.is_authenticated %}
-            <p class="informational informational-fail">Your account doesn't 
have access to this page. To proceed,
-            please login with an account that has access.</p>
-            {% else %}
-            <p>Please login to see this page.</p>
-            {% endif %}
-        {% endif %}
         <form method="post" class="pure-form" action="{{ url('login') }}">
           <input type="hidden" name="csrfmiddlewaretoken" value="{{ csrf_token 
}}" />
           {{ form.username }}
diff --git a/talerbank/app/tests.py b/talerbank/app/tests.py
index f6ccb4b..eff2387 100644
--- a/talerbank/app/tests.py
+++ b/talerbank/app/tests.py
@@ -1,16 +1,19 @@
 #  This file is part of TALER
 #  (C) 2014, 2015, 2016 INRIA
 #
-#  TALER is free software; you can redistribute it and/or modify it under the
-#  terms of the GNU Affero General Public License as published by the Free 
Software
-#  Foundation; either version 3, or (at your option) any later version.
+#  TALER is free software; you can redistribute it and/or modify
+#  it under the terms of the GNU Affero General Public License as
+#  published by the Free Software Foundation; either version 3,
+#  or (at your option) any later version.
 #
-#  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
-#  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 
FOR
-#  A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+#  TALER is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty
+#  of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+#  See the GNU General Public License for more details.
 #
-#  You should have received a copy of the GNU General Public License along with
-#  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+#  You should have received a copy of the GNU General Public
+#  License along with TALER; see the file COPYING.  If not,
+#  see <http://www.gnu.org/licenses/>.
 #
 #  @author Marcello Stanisci
 
@@ -30,7 +33,7 @@ from .views import wire_transfer
 from .amount import Amount, CurrencyMismatch, BadFormatAmount
 
 LOGGER = logging.getLogger()
-LOGGER.setLevel(logging.WARNING)
+LOGGER.setLevel(logging.INFO)
 
 def clear_db():
     User.objects.all().delete()
@@ -145,6 +148,7 @@ class RegisterTestCase(TestCase):
     """User registration"""
 
     def setUp(self):
+        clear_db()
         BankAccount(
             user=User.objects.create_user(
                 username='Bank')).save()
@@ -162,28 +166,6 @@ class RegisterTestCase(TestCase):
         # this assertion tests "/profile""s view
         self.assertEqual(200, response.status_code)
 
-
-class RegisterWrongCurrencyTestCase(TestCase):
-    """User registration"""
-
-    def setUp(self):
-        # Note, config has KUDOS as currency.
-        BankAccount(
-            user=User.objects.create_user(username='Bank'),
-            amount=Amount('WRONGCURRENCY')).save()
-        # Takes account_no = 1, as the first one.
-
-    def tearDown(self):
-        clear_db()
-
-    def test_register(self):
-        client = Client()
-        response = client.post(reverse("register", urlconf=urls),
-                               {"username": "test_register",
-                                "password": "test_register"},
-                               follow=True)
-        self.assertEqual(500, response.status_code)
-
 class LoginTestCase(TestCase):
     """User login"""
 
@@ -211,12 +193,8 @@ class LoginTestCase(TestCase):
             **{"HTTP_X_TALER_BANK_USERNAME": "Wrong",
                "HTTP_X_TALER_BANK_PASSWORD": "Credentials"})
         data = response.content.decode("utf-8")
-        self.assertJSONEqual(
-            '{"error": "Wrong username/password", "ec": 5212}',
-            json.loads(data))
         self.assertEqual(401, response.status_code)
 
-
 class AmountTestCase(TestCase):
 
     def test_cmp(self):
@@ -327,7 +305,9 @@ class AddIncomingTestCase(TestCase):
             **{"HTTP_X_TALER_BANK_USERNAME": "user_user",
                "HTTP_X_TALER_BANK_PASSWORD": "user_password"})
         # note: a bad currency request gets 400.
-        self.assertEqual(400, response.status_code)
+        self.assertRaises(CurrencyMismatch)
+        self.assertEqual(406, response.status_code)
+        LOGGER.info(response.content.decode("utf-8"))
         # Try to go debit
         data = '{"auth": {"type": "basic"}, \
                  "credit_account": 1, \
@@ -342,6 +322,20 @@ class AddIncomingTestCase(TestCase):
             **{"HTTP_X_TALER_BANK_USERNAME": "user_user",
                "HTTP_X_TALER_BANK_PASSWORD": "user_password"})
         self.assertEqual(403, response.status_code)
+        # Try use a non-existent recipient.
+        data = '{"auth": {"type": "basic"}, \
+                 "credit_account": 1987, \
+                 "subject": "TESTWTID", \
+                 "exchange_url": "https://exchange.test";, \
+                 "amount": "%s:1"}' % settings.TALER_CURRENCY
+        response = client.post(
+            reverse("add-incoming", urlconf=urls),
+            data=data,
+            content_type="application/json",
+            follow=True,
+            **{"HTTP_X_TALER_BANK_USERNAME": "user_user",
+               "HTTP_X_TALER_BANK_PASSWORD": "user_password"})
+        self.assertEqual(404, response.status_code)
 
 class HistoryContext:
     def __init__(self, expected_resp, **kwargs):
@@ -354,6 +348,7 @@ class HistoryContext:
 class HistoryTestCase(TestCase):
 
     def setUp(self):
+        clear_db()
         debit_account = BankAccount(
             user=User.objects.create_user(
                 username='User',
@@ -391,17 +386,23 @@ class HistoryTestCase(TestCase):
         for ctx in (
                 HistoryContext(
                     expected_resp={"status": 200},
-                    delta="4", direction="both"),
+                    delta="-4", direction="both"),
                 HistoryContext(
                     expected_resp={
-                        "fields": [("row_id", 6)],
+                        "fields": [("row_id", 9)],
                         "status": 200},
                     delta="+1", start="5", direction="both"),
                 HistoryContext(
                     expected_resp={
-                        "fields": [("wt_subject", "h")],
+                        "fields": [("wt_subject", "c")],
+                        "status": 200},
+                    delta="1", start=2,
+                    direction="both", ordering="ascending"),
+                HistoryContext(
+                    expected_resp={
+                        "fields": [("wt_subject", "a")],
                         "status": 200},
-                    delta="-1", start=9, direction="both"),
+                    delta="-1", start=2, direction="both"),
                 HistoryContext(
                     expected_resp={"status": 204},
                     delta="1", start="11", direction="both"),
@@ -422,7 +423,7 @@ class HistoryTestCase(TestCase):
                     expected_resp={"status": 204},
                     delta="+1", direction="cancel+"),
                 HistoryContext(expected_resp={"status": 200},
-                               delta="+1", direction="debit")):
+                               delta="-1", direction="debit")):
             response = self.client.get(
                 reverse("history", urlconf=urls), ctx.urlargs,
                 **{"HTTP_X_TALER_BANK_USERNAME": "User",
@@ -558,7 +559,8 @@ class ParseAmountTestCase(TestCase):
             ret.dump())
         try:
             Amount.parse("Buggy")
-        except BadFormatAmount:
+        except BadFormatAmount as err:
+            self.assertEqual(err.hint, "Amount given was incorrect")
             return
         # make sure the control doesn't get here
         self.assertEqual(True, False)
@@ -593,7 +595,7 @@ class MeasureHistory(TestCase):
         # Measure the time extract_history() needs to retrieve
         # ~ntransfers records.
         timer = timeit.Timer(
-            stmt="extract_history(self.user_bankaccount0)",
+            stmt="extract_history(self.user_bankaccount0, False)",
             setup="from talerbank.app.views import extract_history",
             globals=locals())
         total_time = timer.timeit(number=1)
@@ -659,7 +661,7 @@ class BalanceTestCase(TestCase):
         response = self.client.get(
             reverse("history", urlconf=urls),
             {"auth": "basic",
-             "delta": 30,
+             "delta": -30,
              "direction": "both",
              "account_number": 55}, # unused
             **{"HTTP_X_TALER_BANK_USERNAME": "U0",
diff --git a/talerbank/app/urls.py b/talerbank/app/urls.py
index 19c0169..c4a543c 100644
--- a/talerbank/app/urls.py
+++ b/talerbank/app/urls.py
@@ -19,6 +19,7 @@
 
 from django.conf.urls import include, url
 from django.views.generic.base import RedirectView
+from django.contrib.auth import views as auth_views
 from . import views
 
 urlpatterns = [
@@ -28,7 +29,11 @@ urlpatterns = [
     url(r'^favicon\.ico$', views.ignore),
     url(r'^admin/add/incoming$', views.add_incoming,
         name="add-incoming"),
-    url(r'^login/$', views.login_view, name="login"),
+    url(r'^login/$',
+        auth_views.LoginView.as_view(
+            template_name="login.html",
+            authentication_form=views.TalerAuthenticationForm),
+        name="login"),
     url(r'^logout/$', views.logout_view, name="logout"),
     url(r'^accounts/register/$', views.register, name="register"),
     url(r'^profile$', views.profile_page, name="profile"),
diff --git a/talerbank/app/views.py b/talerbank/app/views.py
index 1e6eabb..2914341 100644
--- a/talerbank/app/views.py
+++ b/talerbank/app/views.py
@@ -1,7 +1,8 @@
-#  This file is part of TALER
-#  (C) 2014, 2015, 2016 INRIA
+##
+# This file is part of TALER
+# (C) 2014, 2015, 2016 INRIA
 #
-#  TALER is free software; you can redistribute it and/or modify it
+# TALER is free software; you can redistribute it and/or modify it
 # under the terms of the GNU Affero General Public License as
 # published by the Free Software Foundation; either version 3, or
 # (at your option) any later version. TALER is distributed in the
@@ -44,27 +45,55 @@ from .amount import Amount
 from .schemas import validate_data
 LOGGER = logging.getLogger(__name__)
 
+##
+# Constant value for the biggest number the bank handles.
+# This value is just equal to the biggest number that JavaScript
+# can handle (because of the wallet).
+UINT64_MAX = (2**64) - 1
+
+
+##
+# Exception raised upon failing login.
+#
 class LoginFailed(Exception):
     hint = "Wrong username/password"
     http_status_code = 401
 
+##
+# Exception raised when the public history from
+# a ordinary user account is tried to be accessed.
 class PrivateAccountException(Exception):
     hint = "The selected account is private"
     http_status_code = 402
 
+
+##
+# Exception raised when some financial operation goes
+# beyond the limit threshold.
 class DebitLimitException(Exception):
     hint = "Debit too high, operation forbidden."
     http_status_code = 403
 
+##
+# Exception raised when some financial operation is
+# attempted and both parties are the same account number.
+#
 class SameAccountException(Exception):
     hint = "Debit and credit account are the same."
     http_status_code = 403
 
+##
+# Exception raised when someone tries to reject a
+# transaction, but they have no rights to accomplish
+# such operation.
 class RejectNoRightsException(Exception):
     hint = "You weren't the transaction credit account, " \
            "no rights to reject."
     http_status_code = 403
 
+##
+# The authentication for users to log in the bank.
+#
 class TalerAuthenticationForm(
         django.contrib.auth.forms.AuthenticationForm):
     def __init__(self, *args, **kwargs):
@@ -73,37 +102,37 @@ class TalerAuthenticationForm(
         self.fields["username"].widget.attrs["placeholder"] = "Username"
         self.fields["password"].widget.attrs["placeholder"] = "Password"
 
+##
+# Return a empty response.  Used in "/favicon.ico" requests.
+#
 def ignore(request):
     del request
     return HttpResponse()
 
-def login_view(request):
-    fail_message, success_message, hint = get_session_hint(request, 
"login_hint")
-    response = django.contrib.auth.views.login(
-        request,
-        authentication_form=TalerAuthenticationForm,
-        template_name="login.html",
-        extra_context={"user": request.user})
-    if hasattr(response, "context_data"):
-        response.context_data["fail_message"] = fail_message
-        response.context_data["success_message"] = success_message
-        response.context_data["hint"] = hint
-    return response
-
+##
+# Get a flag from the session and clear it.
+#
+# @param request the HTTP request being served.
+# @param name name of the session value that should be retrieved.
+# @return the value, if found; otherwise False.
 def get_session_flag(request, name):
-    """
-    Get a flag from the session and clear it.
-    """
     if name in request.session:
         ret = request.session[name]
         del request.session[name]
         return ret
     return False
 
+
+##
+# Get a hint from the session and clear it.  A 'hint' is a
+# "message" that different parts of the bank can send to each
+# other - via the state - communicating what is the state of
+# the HTTP session.
+#
+# @param request the HTTP request being served.
+# @param name hint name
+# @return the hint (a "null" one if none was found)
 def get_session_hint(request, name):
-    """
-    Get a hint from the session and clear it.
-    """
     if name in request.session:
         ret = request.session[name]
         del request.session[name]
@@ -112,6 +141,10 @@ def get_session_hint(request, name):
     return False, False, None
 
 
+##
+# Build the list containing all the predefined accounts; the
+# list contains, for example, the exchange, the bank itself, and
+# all the public accounts (like GNUnet / Tor / FSF / ..)
 def predefined_accounts_list():
     account = 2
     ret = []
@@ -120,17 +153,41 @@ def predefined_accounts_list():
         account += 1
     return ret
 
-# Thanks to [1]
+##
+# Thanks to [1], this class provides a dropdown menu that
+# can be used within a <select> element, in a <form>.
 class InputDatalist(forms.TextInput):
 
+    ##
+    # Constructor function.
+    #
+    # @param self the object itself.
+    # @param datalist a list of admitted values.
+    # @param name the name of the value that will be sent
+    #        along the POST.
+    # @param args positional arguments
+    # @param kwargs keyword arguments
+    # @return the object
     def __init__(self, datalist, name, *args, **kwargs):
         super().__init__(*args, **kwargs)
         self._name = name
         self._datalist = datalist()
         self.attrs.update(
             {"list": "%slist" % name,
-             "pattern": "[1-9]+"})
-
+             "pattern": "[1-9]+[0-9]*"})
+
+    ##
+    # Method that produces the final HTML from the object itself.
+    #
+    # @param self the object itself
+    # @param name the name of the value that will be sent
+    #        along the POST
+    # @param value value to be sent along the @a name.
+    # @param attrs a dict indicating which HTML attribtues should
+    #        be defined in the rendered element.
+    # @param renderer render engine (left as None, typically); it
+    #        is a class that respects the low-level render API from
+    #        Django, see [2]
     def render(self, name, value, attrs=None, renderer=None):
         html = super().render(
             name, value, attrs=attrs, renderer=renderer)
@@ -142,8 +199,11 @@ class InputDatalist(forms.TextInput):
         return html + datalist
 
 
+##
+# Form for sending wire transfers.  It usually appears in the
+# user profile page.
+#
 class WTForm(forms.Form):
-    '''Form used to wire transfer money internally in the bank.'''
     amount = forms.FloatField(
         min_value=0.1,
         widget=forms.NumberInput(attrs={"class": "currency-input"}))
@@ -152,12 +212,26 @@ class WTForm(forms.Form):
         widget=InputDatalist(predefined_accounts_list, "receiver"))
     subject = forms.CharField()
 
-# Check if user's logged in.  Check if he/she has withdrawn or
-# registered; render profile page.
 
+
+##
+# This method serves the profile page, which is the main
+# page where the user interacts with the bank, and also the
+# page that the user gets after a successful login.  It has
+# to handle the following cases: (1) the user requests the
+# profile page after haing POSTed a wire transfer request.
+# (2) The user requests the page after having withdrawn coins,
+# that means that a wire transfer has been issued to the exchange.
+# In this latter case, the method has to notify the wallet about
+# the operation outcome.  (3) Ordinary GET case, where the
+# straight page should be returned.
+# 
+# @param request Django-specific HTTP request object.
+# @return Django-specific HTTP response object.
 @login_required
 def profile_page(request):
     if request.method == "POST":
+        # WTForm ~ Wire Transfer Form.
         wtf = WTForm(request.POST)
         if wtf.is_valid():
             amount_parts = (settings.TALER_CURRENCY,
@@ -182,7 +256,8 @@ def profile_page(request):
         currency=request.user.bankaccount.amount.currency,
         account_no=request.user.bankaccount.account_no,
         wt_form=wtf,
-        history=extract_history(request.user.bankaccount),
+        history=extract_history(request.user.bankaccount,
+                                True),
     )
     if settings.TALER_SUGGESTED_EXCHANGE:
         context["suggested_exchange"] = settings.TALER_SUGGESTED_EXCHANGE
@@ -197,12 +272,24 @@ def profile_page(request):
     return response
 
 
+##
+# Helper function that hashes its input.  Usually
+# used to hash the response to the math CAPTCHA.
+#
+# @param ans the plain text answer to hash.
+# @return the hashed version of @a ans.
 def hash_answer(ans):
     hasher = hashlib.new("sha1")
     hasher.update(settings.SECRET_KEY.encode("utf-8"))
     hasher.update(ans.encode("utf-8"))
     return hasher.hexdigest()
 
+
+##
+# Helper function that makes CAPTCHA's question and
+# answer pair.
+#
+# @return the question and (hashed) answer pair.
 def make_question():
     num1 = random.randint(1, 10)
     operand = random.choice(("*", "+", "-"))
@@ -219,6 +306,16 @@ def make_question():
     return question, hash_answer(answer)
 
 
+
+
+##
+# This method build the page containing the math CAPTCHA that
+# protects coins withdrawal.  It takes all the values from the
+# URL and puts them into the state, for further processing after
+# a successful answer from the user.
+#
+# @param request Django-specific HTTP request object
+# @return Django-specific HTTP response object
 @require_GET
 @login_required
 def pin_tan_question(request):
@@ -249,6 +346,13 @@ def pin_tan_question(request):
     return render(request, "pin_tan.html", context)
 
 
+
+
+##
+# This method serves the user's answer to the math CAPTCHA,
+# and reacts accordingly to its correctness.  If correct (wrong),
+# it redirects the user to the profile page showing a success
+# (failure) message into the informational bar.
 @require_POST
 @login_required
 def pin_tan_verify(request):
@@ -273,15 +377,25 @@ def pin_tan_verify(request):
     request.session["just_withdrawn"] = True
     return redirect("profile")
 
+
+
+##
+# Class representing the registration form.
 class UserReg(forms.Form):
     username = forms.CharField()
     password = forms.CharField(widget=forms.PasswordInput())
 
 
+
+##
+# This method serves the request for registering a user.
+# If successful, it redirects the user to their profile page;
+# otherwise it will show again the same form (currently, without
+# displaying _any_ error/warning message.)
+#
+# @param request Django-specific HTTP request object.
+# @return Django-specific HTTP response object.
 def register(request):
-    """
-    register a new user giving 100 KUDOS bonus
-    """
     if request.method != "POST":
         return render(request, "register.html")
     form = UserReg(request.POST)
@@ -315,18 +429,42 @@ def register(request):
     return redirect("profile")
 
 
+
+##
+# Logs the user out, redirecting it to the bank's homepage.
+#
+# @param request Django-specific HTTP request object.
+# @return Django-specific HTTP response object.
 def logout_view(request):
-    """
-    Log out the user and redirect to index page.
-    """
     django.contrib.auth.logout(request)
     request.session["login_hint"] = False, True, "Logged out!"
     return redirect("index")
 
 
-def extract_history(account, delta=None, start=-1, sign="+"):
+
+##
+# Build the history array.
+#
+# @param account the bank account object whose history is being
+#        extracted.
+# @param descending currently not used.
+# @param delta how many history entries will be contained in the
+#        array (will be passed as-is to the internal routine
+#        @a query_history).
+# @param start any history will be searched starting from this
+#        value (which is a row ID), and going to the past or to
+#        the future (depending on the user choice).  However, this
+#        value itself will not be included in the history.
+# @param sign this value ("+"/"-") determines whether the history
+#        entries will be younger / older than @a start.
+# @return the history array.
+def extract_history(account,
+                    descending,
+                    delta=None,
+                    start=UINT64_MAX,
+                    sign="+"):
     history = []
-    qs = query_history(account, "both", delta, start, sign)
+    qs = query_history(account, "both", delta, start, sign, True)
     for item in qs:
         if item.credit_account == account:
             counterpart = item.debit_account
@@ -348,9 +486,20 @@ def extract_history(account, delta=None, start=-1, 
sign="+"):
     return history
 
 
+
+##
+# Serve the page showing histories from publicly visible accounts.
+#
+# @param request Django-specific HTTP request object.
+# @param name name of the public account to show.
+# @param page given that public histories are paginated, this
+#        value is the page number to display in the response.
+# @return Django-specific HTTP response object.
 def serve_public_accounts(request, name=None, page=None):
     try:
-        page = int(page)
+        page = abs(int(page))
+        if page == 0:
+            raise Exception
     except Exception:
         page = 1
 
@@ -360,17 +509,23 @@ def serve_public_accounts(request, name=None, page=None):
     if not user.bankaccount.is_public:
         raise PrivateAccountException("Can't display public history for 
private account")
 
-    num_records = query_history_raw(user.bankaccount, "both", start=-1, 
sign="-").count()
-
+    num_records = query_history_raw(user.bankaccount,
+                                    "both",
+                                    start=-1, # makes sign ignored.
+                                    sign="+").count()
     DELTA = 30
-    youngest = 1 + DELTA * (page - 1)
+    # '//' operator is NO floating point.
+    num_pages = max(num_records // DELTA, 1)
 
     public_accounts = BankAccount.objects.filter(is_public=True)
 
-    # Retrieve DELTA records, starting from 'youngest'
-    history = extract_history(user.bankaccount, DELTA, youngest - 1, "+")
+    # Retrieve DELTA records younger than 'start_row' (included).
+    history = extract_history(user.bankaccount,
+                              True,
+                              DELTA * page,
+                              -1,
+                              "+")[DELTA * (page - 1):(DELTA * page)]
 
-    num_pages = max(num_records // DELTA, 1)
     pages = list(range(1, num_pages + 1))
 
     context = dict(
@@ -387,7 +542,14 @@ def serve_public_accounts(request, name=None, page=None):
     )
     return render(request, "public_accounts.html", context)
 
-
+##
+# Decorator function that authenticates requests by fetching
+# the credentials over the HTTP requests headers.
+#
+# @param view_func function that will be called after the
+#        authentication, and that will usually serve the requested
+#        endpoint.
+# @return FIXME.
 def login_via_headers(view_func):
     def _decorator(request, *args, **kwargs):
         user_account = auth_and_login(request)
@@ -397,29 +559,42 @@ def login_via_headers(view_func):
         return view_func(request, user_account, *args, **kwargs)
     return wraps(view_func)(_decorator)
 
-# Internal function used by /history and /public-accounts.  It
-# offers abstraction against the query string definition and DB
-# querying.
+
+##
+# Helper function that sorts in a descending, or ascending
+# manner, the history elements returned by the internal routine
+# @a query_history_raw.
 #
-# 'bank_account': whose history is going to be retrieved.
-# 'direction': (both|credit|debit|cancel+|cancel-).
-# 'delta': how many results are going to be extracted.  If 'None'
-#   is given, no filter of this kind will be applied.
-# 'start': a "id" indicating the first record to be returned.
-#   If -1 is given, then the first record will be the youngest
-#   and 'delta' records will be returned, _regardless_ of the
-#   'sign' being passed.
-# 'sign': (+|-) indicating that we want records younger|older
-#   than 'start'.
-
-def query_history(bank_account, direction, delta, start, sign):
-    qs = query_history_raw(bank_account, direction, start, sign)
-    # '-id' does descending ordering.
-    ordering = "-id" if sign in ["-", "*"] else "id"
-    return qs.order_by(ordering)[:delta]
-
-
-def query_history_raw(bank_account, direction, start, sign):
+# @param bank_account the bank account object whose
+#        history is being extracted.
+# @param direction takes the following three values,
+#        * debit: only entries where the querying user has _paid_
+#                 will be returned.
+#        * credit: only entries where the querying user _got_
+#                  paid will be returned.
+#        * both: both of the cases above will be returned.
+#        * cancel+: only entries where the querying user cancelled
+#                   the _receiving_ of money will be returned.
+#        * cancel-: only entries where the querying user cancelled
+#                   the _paying_ of money will be returned.
+# @param delta how many history entries will be contained in the
+#        array (will be passed as-is to the internal routine
+#        @a query_history).
+# @param start any history will be searched starting from this
+#        value (which is a row ID), and going to the past or to
+#        the future (depending on the user choice).  However, this
+#        value itself will not be included in the history.
+# @param sign this value ("+"/"-") determines whether the history
+#        entries will be younger / older than @a start.
+# @param descending if True, then the results will have the
+#        youngest entry in the first position.
+def query_history(bank_account,
+                  direction,
+                  delta,
+                  start,
+                  sign,
+                  descending=True):
+
     direction_switch = {
         "both": (Q(debit_account=bank_account) |
                  Q(credit_account=bank_account)),
@@ -436,33 +611,45 @@ def query_history_raw(bank_account, direction, start, 
sign):
         "-": Q(id__lt=start),
     }
 
-    # Handle special case.
-    if start == -1: # return 'delta' youngest records.
-        sign = "+"
-
-    return BankTransaction.objects.filter(
+    qs = BankTransaction.objects.filter(
         direction_switch.get(direction),
-        sign_filter.get(sign));
+        sign_filter.get(sign))
+    
+    order = "-id" if descending else "id"
+    return qs.order_by(order)[:delta]
 
 
+##
+# Serve a request of /history.
+#
+# @param request Django-specific HTTP request.
+# @param user_account the account whose history should be gotten.
+# @return Django-specific HTTP response object.
 @require_GET
 @login_via_headers
 def serve_history(request, user_account):
-    """
-    This API is used to get a list of transactions related to one
-    user.
-    """
     validate_data(request, request.GET.dict())
-    # delta
+
+    # delta (it does exist: enforced by the check above.)
     parsed_delta = re.search(r"([\+-])?([0-9]+)",
                              request.GET.get("delta"))
+    # normalize the sign.
     sign = parsed_delta.group(1)
+    sign = sign if sign else "+"
+
+    # Ordering.
+    ordering = request.GET.get("ordering", "descending")
+    start = request.GET.get("start")
+
+    if not start:
+        start = 0 if "+" == sign else UINT64_MAX
 
     qs = query_history(user_account.bankaccount,
                        request.GET.get("direction"),
                        int(parsed_delta.group(2)),
-                       int(request.GET.get("start", -1)),
-                       sign if sign else "+")
+                       int(start),
+                       sign,
+                       "descending" == ordering)
 
     history = []
     cancelled = request.GET.get("cancelled", "show")
@@ -488,6 +675,14 @@ def serve_history(request, user_account):
         return HttpResponse(status=204)
     return JsonResponse(dict(data=history), status=200)
 
+
+##
+# Helper function that authenticates a user by fetching the
+# credentials from the HTTP headers.  Typically called from
+# decorators.
+#
+# @param request Django-specific HTTP request object.
+# @return Django-specific "authentication object".
 def auth_and_login(request):
     """Return user instance after checking authentication
        credentials, False if errors occur"""
@@ -511,6 +706,16 @@ def auth_and_login(request):
         username=username,
         password=password)
 
+
+
+##
+# Serve a request of /reject (for rejecting wire transfers).
+#
+# @param request Django-specific HTTP request object.
+# @param user_account the account that is going to reject the
+#        transfer.  Used to check whether they have this right
+#        or not (only accounts which _got_ payed can cancel the
+#        transaction.)
 @transaction.atomic
 @csrf_exempt
 @require_http_methods(["PUT", "POST"])
@@ -529,17 +734,19 @@ def reject(request, user_account):
     return HttpResponse(status=204)
 
 
+
+##
+# Serve a request to make a wire transfer.  Allows fintech
+# providers to issues payments in a programmatic way.
+#
+# @param request Django-specific HTTP request object.
+# @param user_account the (authenticated) user issuing this
+#        request.
+# @return Django-specific HTTP response object.
 @csrf_exempt
 @require_POST
 @login_via_headers
 def add_incoming(request, user_account):
-    """
-    Internal API used by exchanges to notify the bank
-    of incoming payments.
-
-    This view is CSRF exempt, since it is not used from
-    within the browser, and only over the private admin interface.
-    """
     data = json.loads(request.body.decode("utf-8"))
     validate_data(request, data)
     subject = "%s %s" % (data["subject"], data["exchange_url"])
@@ -554,6 +761,15 @@ def add_incoming(request, user_account):
          "timestamp": "/Date(%s)/" % int(wtrans.date.timestamp())})
 
 
+
+
+##
+# Serve a Taler withdrawal request; takes the amount chosen
+# by the user, and builds a response to trigger the wallet into
+# the withdrawal protocol
+#
+# @param request Django-specific HTTP request.
+# @return Django-specific HTTP response object.
 @login_required
 @require_POST
 def withdraw_nojs(request):
@@ -576,13 +792,21 @@ def withdraw_nojs(request):
             settings.TALER_SUGGESTED_EXCHANGE
     return response
 
+
+##
+# Make a wire transfer between two accounts (internal to the bank)
+#
+# @param amount how much money the wire transfer is worth.
+# @param debit_account the account that gives money.
+# @param credit_account the account that receives money.
+# @return a @a BankTransaction object.
 def wire_transfer(amount, debit_account, credit_account,
                   subject):
-    LOGGER.info("%s => %s, %s, %s" %
-                (debit_account.account_no,
-                 credit_account.account_no,
-                 amount.stringify(2),
-                 subject))
+    LOGGER.debug("%s => %s, %s, %s" %
+                 (debit_account.account_no,
+                  credit_account.account_no,
+                  amount.stringify(2),
+                  subject))
     if debit_account.pk == credit_account.pk:
         LOGGER.error("Debit and credit account are the same!")
         raise SameAccountException()
@@ -638,3 +862,4 @@ def wire_transfer(amount, debit_account, credit_account,
     return transaction_item
 
 # [1] 
https://stackoverflow.com/questions/24783275/django-form-with-choices-but-also-with-freetext-option
+# [2] 
https://docs.djangoproject.com/en/2.1/ref/forms/renderers/#low-level-widget-render-api
diff --git a/talerbank/jinja2.py b/talerbank/jinja2.py
index fc3afc4..d6a1e2b 100644
--- a/talerbank/jinja2.py
+++ b/talerbank/jinja2.py
@@ -1,18 +1,23 @@
-#  This file is part of TALER
-#  (C) 2017 INRIA
+##
+# This file is part of TALER
+# (C) 2017 INRIA
 #
-#  TALER is free software; you can redistribute it and/or modify it under the
-#  terms of the GNU Affero General Public License as published by the Free 
Software
-#  Foundation; either version 3, or (at your option) any later version.
+# TALER is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Affero General Public
+# License as published by the Free Software Foundation; either
+# version 3, or (at your option) any later version.
 #
-#  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
-#  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 
FOR
-#  A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+# TALER is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty
+# of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+# See the GNU General Public License for more details.
 #
-#  You should have received a copy of the GNU General Public License along with
-#  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+# You should have received a copy of the GNU General Public
+# License along with TALER; see the file COPYING.  If not,
+# see <http://www.gnu.org/licenses/>
 #
 #  @author Florian Dold
+#  @brief Extends the Jinja2 API with custom functions.
 
 import os
 import math
@@ -22,10 +27,21 @@ from django.conf import settings
 from jinja2 import Environment
 
 
+##
+# Check if a URL is absolute or not.
+#
+# @param urloc the URL to check.
+# @return True if the URL is absolute, False otherwise.
 def is_absolute(urloc):
     return bool(urlparse(urloc).netloc)
 
 
+##
+# Join URL components held in a list, taking care
+# of not having double slashes in the result.
+#
+# @param parts list of URL components.
+# @return the string made of the joined components
 def join_urlparts(*parts):
     ret = ""
     part = 0
@@ -40,16 +56,37 @@ def join_urlparts(*parts):
     return ret
 
 
+##
+# Prefixes the argument with the location for static content.
+#
+# @param urloc the URL portion that should be prefixed; in
+#        other words, this will be in final position in the
+#        produced result.
+# @return the URL that picks @a urloc from the location for
+#         static content.
 def static(urloc):
     if is_absolute(urloc):
         return urloc
     return join_urlparts(get_script_prefix(), settings.STATIC_URL, urloc)
 
 
+##
+# Helper function that fetches a value from the settings.
+#
+# @param name the name to lookup in the settings.
+# @return @a name's value as defined in the settings, or
+#         a empty string otherwise.
 def settings_value(name):
     return getattr(settings, name, "")
 
 
+
+##
+# Fetch the URL given its "name".
+#
+# @param url_name URL's name as defined in urlargs.py
+# @param kwargs key-value list that will be appended
+#        to the URL as the parameter=value pairs.
 def url(url_name, **kwargs):
     # strangely, Django's 'reverse' function
     # takes a named parameter 'kwargs' instead
@@ -57,15 +94,32 @@ def url(url_name, **kwargs):
     return reverse(url_name, kwargs=kwargs)
 
 
+##
+# Helper function that reads a value from the environment.
+#
+# @param name env value to read
+# @return the value, or None if not found.
 def env_get(name, default=None):
     return os.environ.get(name, default)
 
 
+##
+# Jinja2 specific function used to activate the definitions
+# of this module.
+#
+# @param options opaque argument (?) given from the caller.
+# @return Jinja2-specific object that contains the new definitions.
 def is_valid_amount(amount):
     if math.isnan(amount.value):
         return False
     return True
 
+
+##
+# Stringifies amount.
+#
+# @param amount amount object.
+# @return amount pretty string.
 def amount_stringify(amount):
     return amount.stringify(settings.TALER_DIGITS, pretty=True)
 
diff --git a/talerbank/settings.py b/talerbank/settings.py
index e8f226d..159a24c 100644
--- a/talerbank/settings.py
+++ b/talerbank/settings.py
@@ -41,7 +41,11 @@ if not SECRET_KEY:
     SECRET_KEY = base64.b64encode(os.urandom(32)).decode('utf-8')
 
 # SECURITY WARNING: don't run with debug turned on in production!
-DEBUG = True
+
+if "demo" == os.environ.get("TALER_ENV_NAME"):
+    DEBUG = False
+else:
+    DEBUG = True
 
 ALLOWED_HOSTS = ["*"]
 
diff --git a/talerbank/talerconfig.py b/talerbank/talerconfig.py
index 5d42adc..69d06a8 100644
--- a/talerbank/talerconfig.py
+++ b/talerbank/talerconfig.py
@@ -1,22 +1,21 @@
-#  This file is part of TALER
-#  (C) 2016 INRIA
+##
+# This file is part of TALER
+# (C) 2016 INRIA
 #
-#  TALER is free software; you can redistribute it and/or modify it under the
-#  terms of the GNU Affero General Public License as published by the Free 
Software
-#  Foundation; either version 3, or (at your option) any later version.
+# TALER is free software; you can redistribute it and/or modify it under the
+# terms of the GNU Affero General Public License as published by the Free 
Software
+# Foundation; either version 3, or (at your option) any later version.
 #
-#  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
-#  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 
FOR
-#  A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+# TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
 #
-#  You should have received a copy of the GNU General Public License along with
-#  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+# You should have received a copy of the GNU General Public License along with
+# TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
 #
-#  @author Florian Dold
-
-"""
-Parse GNUnet-style configurations in pure Python
-"""
+# @author Florian Dold
+# @author Marcello Stanisci
+# @brief Parse GNUnet-style configurations in pure Python
 
 import logging
 import collections
@@ -31,6 +30,7 @@ LOGGER = logging.getLogger(__name__)
 __all__ = ["TalerConfig"]
 
 TALER_DATADIR = None
+
 try:
     # not clear if this is a good idea ...
     from talerpaths import TALER_DATADIR as t
@@ -38,21 +38,34 @@ try:
 except ImportError:
     pass
 
+##
+# Exception class for a any configuration error.
 class ConfigurationError(Exception):
     pass
 
+##
+# Exception class for malformed strings having with parameter
+# expansion.
 class ExpansionSyntaxError(Exception):
     pass
 
-
+##
+# Do shell-style parameter expansion.
+# Supported syntax:
+#  - ${X}
+#  - ${X:-Y}
+#  - $X
+#
+# @param var entire config value that might contain a parameter
+#        to expand.
+# @param getter function that is in charge of returning _some_
+#        value to be used in place of the parameter to expand.
+#        Typically, the replacement is searched first under the
+#        PATHS section of the current configuration, or (if not
+#        found) in the environment.
+#
+# @return the expanded config value.
 def expand(var: str, getter: Callable[[str], str]) -> str:
-    """
-    Do shell-style parameter expansion.
-    Supported syntax:
-    - ${X}
-    - ${X:-Y}
-    - $X
-    """
     pos = 0
     result = ""
     while pos != -1:
@@ -89,11 +102,24 @@ def expand(var: str, getter: Callable[[str], str]) -> str:
         result = result + replace
         pos = end
 
-
     return result + var[pos:]
 
-
+##
+# A configuration entry.
 class Entry:
+
+    ##
+    # Init constructor.
+    #
+    # @param self the object itself.
+    # @param config reference to a configuration object - FIXME
+    #        define "configuration object".
+    # @param section name of the config section where this entry
+    #        got defined.
+    # @param option name of the config option associated with this
+    #        entry.
+    # @param kwargs keyword arguments that hold the value / filename
+    #        / line number of this current option.
     def __init__(self, config, section: str, option: str, **kwargs) -> None:
         self.value = kwargs.get("value")
         self.filename = kwargs.get("filename")
@@ -102,13 +128,35 @@ class Entry:
         self.option = option
         self.config = weakref.ref(config)
 
+    ##
+    # XML representation of this entry.
+    #
+    # @param self the object itself.
+    # @return XML string holding all the relevant information
+    #         for this entry.
     def __repr__(self) -> str:
         return "<Entry section=%s, option=%s, value=%s>" \
                % (self.section, self.option, repr(self.value),)
 
+    ##
+    # Return the value for this entry, as is.
+    #
+    # @param self the object itself.
+    # @return the config value.
     def __str__(self) -> Any:
         return self.value
 
+    ##
+    # Return entry value, accepting defaults.
+    #
+    # @param self the object itself
+    # @param default default value to return if none was found.
+    # @param required indicate whether the value was required or not.
+    #        If the value was required, but was not found, an exception
+    #        is found.
+    # @param warn if True, outputs a warning message if the value was
+    #        not found -- regardless of it being required or not.
+    # @return the value, or the given @a default, if not found.
     def value_string(self, default=None, required=False, warn=False) -> str:
         if required and self.value is None:
             raise ConfigurationError("Missing required option '%s' in section 
'%s'" \
@@ -124,6 +172,15 @@ class Entry:
             return default
         return self.value
 
+    ##
+    # Return entry value as a _int_.  Raise exception if the
+    # value is not convertible to a integer.
+    #
+    # @param self the object itself
+    # @param default currently ignored.
+    # @param required currently ignored.
+    # @param warn currently ignored.
+    # @return the value, or the given @a default, if not found.
     def value_int(self, default=None, required=False, warn=False) -> int:
         value = self.value_string(default, warn, required)
         if value is None:
@@ -133,7 +190,12 @@ class Entry:
         except ValueError:
             raise ConfigurationError("Expected number for option '%s' in 
section '%s'" \
                                      % (self.option.upper(), 
self.section.upper()))
-
+    ##
+    # Fetch value to substitute to expansion variables.
+    #
+    # @param self the object itself.
+    # @param key the value's name to lookup.
+    # @return the value, if found, None otherwise.
     def _getsubst(self, key: str) -> Any:
         value = self.config()["paths"][key].value
         if value is not None:
@@ -143,57 +205,136 @@ class Entry:
             return value
         return None
 
+    ##
+    # Fetch the config value that should be a filename,
+    # taking care of invoking the variable-expansion logic first.
+    #
+    # @param self the object itself.
+    # @param default currently ignored.
+    # @param required currently ignored.
+    # @param warn currently ignored.
+    # @return the (expanded) filename.
     def value_filename(self, default=None, required=False, warn=False) -> str:
         value = self.value_string(default, required, warn)
         if value is None:
             return None
         return expand(value, self._getsubst)
 
+    ##
+    # Give the filename and line number of this config entry.
+    #
+    # @param self this object.
+    # @return <filename>:<linenumber>, or "<unknown>" if one
+    #         is not known.
     def location(self) -> str:
         if self.filename is None or self.lineno is None:
             return "<unknown>"
         return "%s:%s" % (self.filename, self.lineno)
 
-
+##
+# Represent a section by inheriting from 'defaultdict'.
 class OptionDict(collections.defaultdict):
+
+    ##
+    # Init constructor.
+    #
+    # @param self the object itself
+    # @param config the "config" object -- typically a @a TalerConfig instance.
+    # @param section_name the section name to assign to this object.
     def __init__(self, config, section_name: str) -> None:
         self.config = weakref.ref(config)
         self.section_name = section_name
         super().__init__()
+
+    ##
+    # Logic to run when a non-existent key is dereferenced.
+    # Just create and return a empty config @a Entry.  Note
+    # that the freshly created entry will nonetheless put
+    # under the accessed key (that *does* become existent
+    # afterwards).
+    #
+    # @param self the object itself.
+    # @param key the key attempted to be accessed.
+    # @return the no-value entry.
     def __missing__(self, key: str) -> Entry:
         entry = Entry(self.config(), self.section_name, key)
         self[key] = entry
         return entry
+
+    ##
+    # Attempt to fetch one value from the object.
+    #
+    # @param self the object itself.
+    # @param chunk the key (?) that is tried to access.
+    # @return the object, if it exists, or a freshly created
+    #         (empty) one, if it doesn't exist.
     def __getitem__(self, chunk: str) -> Entry:
         return super().__getitem__(chunk.lower())
+
+    ##
+    # Set one value into the object.
+    #
+    # @param self the object itself.
+    # @param chunk key under which the value is going to be set.
+    # @param value value to set the @a chunk to.
     def __setitem__(self, chunk: str, value: Entry) -> None:
         super().__setitem__(chunk.lower(), value)
 
-
+##
+# Collection of all the (@a OptionDict) sections.
 class SectionDict(collections.defaultdict):
+
+    ##
+    # Automatically invoked when a missing section is
+    # dereferenced.  It creates the missing - empty - section.
+    #
+    # @param self the object itself.
+    # @param key the dereferenced section name.
+    # @return the freshly created section.
     def __missing__(self, key):
         value = OptionDict(self, key)
         self[key] = value
         return value
+
+    ##
+    # Attempt to retrieve a section.
+    #
+    # @param self the object itself.
+    # @param chunk the section name.
     def __getitem__(self, chunk: str) -> OptionDict:
         return super().__getitem__(chunk.lower())
+
+    ##
+    # Set a section.
+    #
+    # @param self the object itself.
+    # @param chunk the section name to set.
+    # @param value the value to set under that @a chunk.
     def __setitem__(self, chunk: str, value: OptionDict) -> None:
         super().__setitem__(chunk.lower(), value)
 
-
+##
+# One loaded taler configuration, including base configuration
+# files and included files.
 class TalerConfig:
-    """
-    One loaded taler configuration, including base configuration
-    files and included files.
-    """
+
+    ##
+    # Init constructor..
+    #
+    # @param self the object itself.
     def __init__(self) -> None:
-        """
-        Initialize an empty configuration
-        """
         self.sections = SectionDict() # just plain dict
 
-    # defaults != config file: the first is the 'base'
-    # whereas the second overrides things from the first.
+    ##
+    # Load a configuration file, instantiating a config object.
+    #
+    # @param filename the filename where to load the configuration
+    #        from.  If None, it defaults "taler.conf".
+    # @param load_defaults if True, then defaults values are loaded
+    #        (from canonical directories like "<prefix>/share/config.d/taler/")
+    #        before the actual configuration file.  This latter then
+    #        can override some/all the defaults.
+    # @return the config object.
     @staticmethod
     def from_file(filename=None, load_defaults=True):
         cfg = TalerConfig()
@@ -205,21 +346,56 @@ class TalerConfig:
                 filename = os.path.expanduser("~/.config/taler.conf")
         if load_defaults:
             cfg.load_defaults()
-        cfg.load_file(filename)
+        cfg.load_file(os.path.expanduser(filename))
         return cfg
 
+    ##
+    # Get a string value from the config.
+    #
+    # @param self the config object itself.
+    # @param section the section to fetch the value from.
+    # @param option the value's option name.
+    # @param kwargs dict argument with instructions about
+    #        the value retrieval logic.
+    # @return the wanted string (or a default / exception if
+    #         a error occurs).
     def value_string(self, section, option, **kwargs) -> str:
         return self.sections[section][option].value_string(
             kwargs.get("default"), kwargs.get("required"), kwargs.get("warn"))
 
+    ##
+    # Get a value from the config that should be a filename.
+    # The variable expansion for the path's components is internally managed.
+    #
+    # @param self the config object itself.
+    # @param section the section to fetch the value from.
+    # @param option the value's option name.
+    # @param kwargs dict argument with instructions about
+    #        the value retrieval logic.
+    # @return the wanted filename (or a default / exception if
+    #         a error occurs).
     def value_filename(self, section, option, **kwargs) -> str:
         return self.sections[section][option].value_filename(
             kwargs.get("default"), kwargs.get("required"), kwargs.get("warn"))
 
+    ##
+    # Get a integer value from the config.
+    #
+    # @param self the config object itself.
+    # @param section the section to fetch the value from.
+    # @param option the value's option name.
+    # @param kwargs dict argument with instructions about
+    #        the value retrieval logic.
+    # @return the wanted integer (or a default / exception if
+    #         a error occurs).
     def value_int(self, section, option, **kwargs) -> int:
         return self.sections[section][option].value_int(
             kwargs.get("default"), kwargs.get("required"), kwargs.get("warn"))
 
+    ##
+    # Load default values from canonical locations.
+    #
+    # @param self the object itself.
     def load_defaults(self) -> None:
         base_dir = os.environ.get("TALER_BASE_CONFIG")
         if base_dir:
@@ -237,15 +413,24 @@ class TalerConfig:
             return
         LOGGER.warning("no base directory found")
 
+    ##
+    # Load configuration from environment variable
+    # TALER_CONFIG_FILE or from default location if the
+    # variable is not set.
+    #
+    # @param args currently unused.
+    # @param kwargs kwargs for subroutine @a from_file.
+    # @return freshly instantiated config object.
     @staticmethod
     def from_env(*args, **kwargs):
-        """
-        Load configuration from environment variable TALER_CONFIG_FILE
-        or from default location if the variable is not set.
-        """
         filename = os.environ.get("TALER_CONFIG_FILE")
         return TalerConfig.from_file(filename, *args, **kwargs)
 
+    ##
+    # Load config values from _each_ file found in a directory.
+    #
+    # @param self the object itself.
+    # @param dirname the directory to crawl in the look for config files.
     def load_dir(self, dirname) -> None:
         try:
             files = os.listdir(dirname)
@@ -257,6 +442,10 @@ class TalerConfig:
                 continue
             self.load_file(os.path.join(dirname, file))
 
+    ##
+    # Load config values from a file.
+    #
+    # @param filename config file to take the values from.
     def load_file(self, filename) -> None:
         sections = self.sections
         try:
@@ -272,6 +461,16 @@ class TalerConfig:
                     if line.startswith("#"):
                         # comment
                         continue
+                    if line.startswith("@INLINE@"):
+                        pair = line.split()
+                        if 2 != len(pair):
+                            LOGGER.error("invalid inlined config filename 
given ('%s')" % line)
+                            continue 
+                        if pair[1].startswith("/"):
+                            self.load_file(pair[1])
+                        else:
+                            
self.load_file(os.path.join(os.path.dirname(filename), pair[1]))
+                        continue
                     if line.startswith("["):
                         if not line.endswith("]"):
                             LOGGER.error("invalid section header in line %s: 
%s",
@@ -300,7 +499,15 @@ class TalerConfig:
             LOGGER.error("Configuration file (%s) not found", filename)
             sys.exit(3)
 
-
+    ##
+    # Dump the textual representation of a config object.
+    # 
+    # Format:
+    # 
+    # [section]
+    # option = value # FIXME (what is location?)
+    #
+    # @param self the object itself, that will be dumped.
     def dump(self) -> None:
         for kv_section in self.sections.items():
             print("[%s]" % (kv_section[1].section_name,))
@@ -310,6 +517,15 @@ class TalerConfig:
                        kv_option[1].value,
                        kv_option[1].location()))
 
+
+    ##
+    # Return a whole section from this object.
+    #
+    # @param self the object itself.
+    # @param chunk name of the section to return.
+    # @return the section - note that if the section is
+    #         not found, a empty one will created on the fly,
+    #         then set under 'chunk', and returned.
     def __getitem__(self, chunk: str) -> OptionDict:
         if isinstance(chunk, str):
             return self.sections[chunk]

-- 
To stop receiving notification emails like this one, please contact
address@hidden



reply via email to

[Prev in Thread] Current Thread [Next in Thread]