This is work in progress and will hopefully evolve into something useful the next few days

Why I'm writing this? To get myself a better understanding of the way Mantissa ticks.
For the very reason writing this, some things got already more obvious.
And if it is helpful for somebody else too, it has more than served its purpose.

I'm not a native English speaker, if there are formulations too stilted correct them please.
This is valid of course for any errors due to my missing comprehension too.
And as usual, if you find typos you are granted to keep them.

HowTo write a Mantissa application…

Intro or Why another document?

So you have found your way here, perhaps reading through the other pages on this wiki, following irc-logs and blog-postings and you are still uncertain what this talking about Mantissa and Axiom is and what these things can do for you? Or is it what you can do with them?

Probably we have a few things in common:

  • you havn't imbibed Python from your's infancy - I'm an old C programmer, started with that kind of stuff nearly 25 years ago
  • but the language gripped you and maybe you even had physical contact with Twisted and Nevow which are some sort of Mantissa foundations ( and/or inspiration?).
  • still technical terms and other slang produce (some) trouble, but in a dark meander of your brain there is something nudging you that such a thing like a multi-protocol, objects manipulating, event-driven ... application server is a too interesting find to throw it back into the deep net ocean
  • on the other hand you want to build an application and if you don't want to begin with Adam and Eve you need to choose a tool for this job. Maybe you had your time with Zope, looked at other Python frameworks, observed TurboGears, Django and even RubyonRails. But they are/seem to be too web-orientated, too web-focussed. Which is quite ok in todays Web 2.0 wonderland, but you and I know that the browser isn't the Internet and that there are a lot more ways to interchange information, and many things that existed long before someone put three 'w's in a row. (some comparison between them, even if they are only web-frameworks, maybe in a later chapter?)

Sounds familiar? Ok, then let's walk a few steps together on my witty trail towards our first Mantissa app.

Small sidekick:

Btw. don't get me wrong, I like the idea of a browser being a client platform or framework.
And having a semantic web. And I'm a big fan of convergence!
But does every coffee mug needs its web (user) interface?
Even houses have web-interfaces nowdays to monitor them - and I know
quite something about this issue - but the heating system is still broken.
Shiny interfaces can only obfuscate basic errors in application design, not fix them. 8)

Vexed about my babbling? Not even amused or at least thought-provoked? Then the following isn't for you, as you can probably do everything even before breakfast. ;-)

Enough words, let's dive into some action. (find a better translation?)

Let the games begin!

Why Mantissa? What's the difference?

When I first read about Mantissa, I think it was this blog entry announcing v0.1 or on the TwistedMatrix mailing-list, the slogan Mantissa Is The Deployment Target caught my attention.

In ancient times strikt separation between GUI and application logic was the key, and a virtue. Something that got lost over the web-app gold-rush years, over the raise of such monstrosities like PHP et al. In this respect the announce of an application server, that seems to not only separate the presentation envelopment but also the protocol envelopment certainly has something inspiring.

(probably more here, but let's work on the structure now)

Theory of Forms

Behind every good computer program stands a good idea.

Idea in the meaning of a flash of a genius, which might make the program even successful.
And idea in the meaning of Plato's ideals, which might make the program even useful.

(at least something about application design and why Mantissa could enforce a good one and still not spoon-feeding the programmer too much)

Prologue

"Hey" - I hear you say - "more than a page of monologue, and no single line of Python?".
You are right, but I think it is worth to first think about what to do and why doing it in this or in that way.
And you are right again, I shouldn't bore you to death with theory, let's start with something more substantial.

Let me expatiate my little idea.

My son, eight years, asked me some time ago for an interactive map where one could mark places where friends live, where we have been and which he wants to visit once. Something like the ones that hang in offices on the wall, the spots marked with little flags or needles with colored heads.

Perhaps something to enqueue in the growing row of apps sitting upon Google Maps, but a few days ago I stumbled upon someones implementation of a worldclock displaying the time for some cities on a equirectangular projection of satellite images, NASA's stunning Blue Marble. I had to modify the worldclock into a mere JavaScript implementation and while doing this I thought about personalizing the view, the points of interest, a way to interactively add new POIs, to socialize them. And while I started with a second Athena/Nevow based version I realized that this could be a nice sandbox project for Mantissa, giving me a reason looking deeper into it.

And while this little idea isn't something that would really fit into its first meaning, at least it should look well in the second.

The Drama 1st Act - looking at the blue-print

Computer applications haven't changed that much over the last 30 years.

Got me, that was a joke - but as most of the times, there is some truth in there. My first program I wrote on a tty - in the literal sense, a tele typewriter. Is was a simple calculator, with input and output printed on paper. For the second one I was granted access to a "real "terminal and recieved an 8" floppy for data storage.

These programs accepted some data, worked on it, presented some output and even stored it to let it survive the program's termination.

If you look from a point high enough, not that much difference today either.

Ok, the complexity and the amount of data changed, we added a lot of onion skins for data acceptance and presentation, and storage has grown up since the days of my old 8" floppy, but if you look at the onion's core... *shrug*.

Now if we look at our little core, what do we have:

  • places which we want to project onto a picture of the earth. These need as mandatory attributes: Longitude, Latitude, some descriptive Name and their Timezone (UTC difference). We need to store these places and provide a way to modify or remove them
  • on presentation of these places we need to calculate their position on the picture based on Longitude and Latitude, and of course the basic parameters of the picture itself

(hmm, I think I change the structure of this document, let us start with the simplest possible app and try to evolve it)

For no particular reason we choose the Python interpreter and Axiom as the weapons of choice for our first steps.

from axiom.store import Store

world = Store('world.axiom')

Nothing special, in database speak we just defined one - a database called 'world'. And if we look outside our Python interpreter we see a directory with that name generated in the current directory.

Now let's populate our world with places, or better first a place to store them (*sigh*) - we create a table.

from axiom.item import Item
from axiom.attributes import text, integer

class Place(Item):
    """ definition for a place in our world """

    schemaVersion = 1
    typeName = 'place'

    """ the descriptive name for our place """
    name = text( allowNone=False, indexed=True)


    """ for the first shot we just define here
        the time difference in minutes """
    utc_diff = integer( default=0)

    """ Longitude - to simplify matters we encode the Decimal Degree notation
                    into one integer, multiplied with 1.000.000
                    positive is East, negative is West
    """
    longitude = integer( default=0)

    """ Latitude - same encoding as with the Longitude
                   positive is North, negative is South
    """
    latitude = integer( default=0)

    def __repr__(self):
        return '<Place name="%s" longitude="%d" latitude="%d" utc_diff="%d">' \
               % (self.name, self.longitude, self.latitude, self.utc_diff)

Therewith we have the most basic methods to store, update and delete our places.
Let's add my hometown:

pf = Place( store=world, name=u'Pforzheim', longitude=8700000, latitude=48883300)

Oops, we have forgotten the timezone. No worries, the change is easy:

pf.utc_diff = 60

Now add some more:

Place( store=world, name=u'New York', longitude=-74000000, latitude=40666667, utc_diff=300)
Place( store=world, name=u'Paris', longitude=2333333, latitude=48850000, utc_diff=60)
Place( store=world, name=u'Kuala Lumpur', longitude=101702000, latitude=3155833, utc_diff=480)
Place( store=world, name=u'Sydney', longitude=151200000, latitude=-33950000, utc_diff=600)

What have we got so far:

for p in world.query( Place):
    print p

<Place name="Pforzheim" longitude="8700000" latitude="48883300" utc_diff="60">
<Place name="New York" longitude="-74000000" latitude="40666667" utc_diff="300">
<Place name="Paris" longitude="2333333" latitude="48850000" utc_diff="60">
<Place name="Kuala Lumpur" longitude="101702000" latitude="3155833" utc_diff="480">
<Place name="Sydney" longitude="151200000" latitude="-33950000" utc_diff="600">

Hmm, two cities in Europe? Let's for the sake of this example remove my hometown from the world and add a city in Africa.

p = world.findFirst( Place, name=u'Pforzheim')
p.deleteFromStore()

Place( store=world, name=u'Johannesburg', longitude=28066667, latitude=26200000, utc_diff=120)

for p in world.query( Place):
    print p

<Place name="New York" longitude="-74000000" latitude="40666667" utc_diff="300">
<Place name="Paris" longitude="2333333" latitude="48850000" utc_diff="60">
<Place name="Kuala Lumpur" longitude="101702000" latitude="3155833" utc_diff="480">
<Place name="Sydney" longitude="151200000" latitude="-33950000" utc_diff="600">
<Place name="Johannesburg" longitude="28066667" latitude="26200000" utc_diff="120">

Btw. if we are only interested in cities in the Northern Hemisphere, we can narrow the query in supplying one or more attribute restrictions.

for p in world.query( Place, Place.latitude > 0):
    print p

<Place name="New York" longitude="-74000000" latitude="40666667" utc_diff="300">
<Place name="Paris" longitude="2333333" latitude="48850000" utc_diff="60">
<Place name="Kuala Lumpur" longitude="101702000" latitude="3155833" utc_diff="480">

And only the South Eastern quarter:

from axiom.attributes import AND

for p in world.query( Place, AND( Place.latitude <= 0, Place.longitude > 0)):
    print p

<Place name="Sydney" longitude="151200000" latitude="-33950000" utc_diff="600">
<Place name="Johannesburg" longitude="28066667" latitude="-26200000" utc_diff="120">

Ok, now we have the very small core of our application. A few blemishes do exist nevertheless.

When adding a place to the store there is nothing in place to avoid duplicate names. At the moment, one should use Axiom's findOrCreate method.

Saving only the time difference towards UTC is a too simple decision. Think only about daylight savings time change-over. A better solution is to save the real time zone and do the calculation with the help of pytz, which is needed for Mantissa anyway.

So what are the next steps?
We'll try to:

  • inject our little core into a Mantissa germ cell
  • add a public accessible web(/Nevow?) instance, serving the static BlueMarble image and the JavaScript bootstrap, but now with the spots extracted out of the Places (table) of our world.

Second Act - there's a light

Before we can start, we need to talk a bit about the structure of a Mantissa app.

Mantissa provides us with several locks our app can dock at. Actually it is the other way around, we have to provide the hooks axiomatic/Mantissa can pick up so we can link arms with Mantissa.

This is done via a plugin, that is loaded via axiomatic into Mantissa. (find better words)

  • a plugin for mantissa
    • --> offer = offering.Offering( name, description, siteRequirements = (), appPowerups = (), benefactorFactories = ())

(explain the parameters)

(don't forget to mention PYTHONPATH)

Mantissa is similar - if you pardon my pictured comparison - to an office space provider: you get all the communication infrastructure, a place to sprawl out, receptionist, switchboard, a meeting place, and even a plate in the lobby.
But to get this going smoothly, you need to have some information about you at their's disposal. Otherwise you sit at some place pretty anonymous and forgotten.

(examples of what we need for what case)

For our simple app, serving only a public page:

  • the mantissa plugin, telling Mantissa what to do if the public page is called, in particular pointing to our app
  • our app itself

(have a basic app up and running)

(add authentication and authorization)

(edit Places through the web)

more to come

(administrative tasks)

(database backup, cold or hot?)

(Mantissa specifics, signup, login, personalization,... )

(polishing the surface(s) )

(schema upgrades)

(interact with other services, jabber, rss, google map?)

jethro@divmod.org