Future Deployer Docs
--------------------

The default application directory of sofoterm is ~/sofoterm, but you can set
something else by setting the environment variable SOFOFILES.

The thing doesn't start?  Check ~/sofoterm/logs/current.output.

Development Mode
----------------

If you're working on this code, you'll definitely want to use development 
mode.  Say::

	sudo python setup.py develop

and everything you edit will immediately affect the installed version.
Without this, you will not have much fun with the tests and such.


Development Server
------------------

There's res_testserver.py in the tests subdirectory.  It feeds a couple
of things to the DB and then runs a server on localhost:8888.  This
is the server the test suite uses.  It uses a temporary db in memory.
There's a sponsor defined in it with key "potz".

What's defined can be seen in a couple of functions in res_testserver
itself.  Also, things are arranged such that the time for the test
server is always some date in 2010-02.

Events with ids 1, 2, ..., 100 should be "stable" and guaranteed to be
be unchanged after a test.  Events with larger ids can be tampered
with freely but must never have any tag after a test.


Logging
-------

We use twisted's logging; for running servers, logging is set up in
in cli.runServer.  In debug mode, we log to stderr, otherwise to rotating
files in sofoterm/logs (cf. base.common.getLoggingDir).

Use the twisted.python.log functions.  Everything is in one log.  Does
this bother anyone?


Times
-----

Times are tricky in sofoterm, since we're dealing both with user times
(that ideally are not touched at all) and with "real" times (e.g., for
HTTP purposes).  It is my intention to keep the two as separate as
humanly possible.

So, user times are always local time and stored in naive datetime instances,
whereas "real" times should be unix epochs ("floats").  The latter are 
stored, e.g., in the lastChanged field in the database and are used
in the interface to (parse|format)HTTPDate.

You should always use base.now() to figure out the current time.  If you
pass utc=True to it, you will get a time float in UTC rather than a
datetime in local time.


Caching/Conditional rendering
-----------------------------

Since we have many pages and their creation is relatively expensive, we
use the last-modified/if-modified-since mechanism quite aggressively, in
particular in the archive.

All pages are free to call EventPage's _setLastChange method.  If they do,
a last-modified header will be added.  Also, before the actual rendering
of the page takes place (in EventPage.renderHTTP), a check will take
place whether the user agent still has a "current" copy, and a NotChanged
exception will be raised if so (this only works for non-sponsors and
GET requests).

The trouble is to figure out the argument to _setLastChange, since 
really a response depends on the server software, settings, templates,
and the database content.  Being "correct" here would deny most of the
use, so we are "aggressive" -- archived content always has a modification
date corresponding to the most recent event that goes into it.

Current events take their lastChanged from the database; current list pages
depend on the last change of the database, counted by edit operations from the
web or server reloads (e.g., after archiving).  This date is available as
conn.lastUpdate.

In addition, we internally cache some pages from server reload/edit to
server reload/edit.  This is to relieve server load since actually rendering
pages ist expensive.  The action appens in pages/caching.  You have to 
manually configure which (GET) URLs get cached using PageCache's addCacheable
method.  Right now, there's a list pages.root.cacheablePaths that you
should edit.  This is probably going to be replaced by some other mechanism
(where the EventPage declares its cacheability or somesuch thing).

To make all this manageable, there are the reload and edit hooks that the
connection listens to to update lastUpdate.




Test Plan
---------

**No commit without test!**  (or branch if you have to commit something
that doesn't work).  We want the code in the repository to at least pass
all unit tests.  The commit shell script in the distro root does
everything necessary.

All tests reside in the tests subdirectory.  Let's see if we want
doctests, if we do, MD has code to collect them into unittest
testsuites.

Since we're using twisted, we have two types of tests: The synchronous
ones, that run without a reactor, and the asynchronous ones, executed by
trial.

To define a new sync tests, write them into some file ending with
test.py; derive from testhelpers.VerboseTest and make the module execute
verbosetest.main when run as a script (see modeltest.py as an example).

The async tests are more fun.  They are executed by a program called
trial that's part of twisted.  Let's discuss this with test_pages
(you'll most likely add your tests there).

You can say either of::

	trial test_pages.py
  trial test_pages.RenderTest
  trial test_pages.RenderTest.testPlaceExpansion

to run all tests, those in RenderTest, or just testPlaceExpansion


Source Format, Encodings
------------------------

Ideally, everything that needs encodings should be in templates
and gettext files that declare the encoding as necessary and otherwise 
are in UTF-8.

Until we actually move to gettext, all source files are in iso-8859-1, with
strings actually containing non-ASCII converted to unicode strings
at construction (common._ is your friend).

Source files *currently* use tabs for indentation (except the imported
code in formal with its standards-conforming four spaces).  If and when
people really feel the need, it's easy to move to four spaces.


Hooks
-----

The following hooks are in use by the core components of sofoterm:

* edit(conn, event) -- when the *web interface* changes an event (these
  are *not* posted for changes from the infrastructure, e.g. on archiving)
* reload(conn) -- sofoterm reload and similar operations.

Changing the data model
-----------------------

Since we don't really use an ORM, changing the data model is a bit of a pain.
Here's what to do:

* Apply your changes to model/event.py
* Apply your changes to model/dbinter.py, figuring out what instructions are
  required to bring the db structure to what you're defining
* Use res_testserver.py to play around with your changes; this does not
  touch anything on disk, so you're free to mess back and forth
* Add an Updater to model/updates.py with the current model version+1 as
  version attribute.
* Increment the MODEL_VERSION global in dbinter.py
* Ideally, stop the server.
* Run sofoterm show modelVersion.  This should run a while and then
  write the new version number.
* If everything works, remove the backups in ~/sofoterm (or whereever)