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)