Configuring and Extending MOAI ============================== The MOAI code can be easily extended in several ways. - Create a buildout for your custom product, that pulls in MOAI - Add some python files in the extensions directory of MOAI - Add some python files to another directory, and register this directory in the buildout The first way is prefered, because you can then easily maintain your own setup in your versioning control system, and deploy just by running a buildout. The second and third way might be easier if you're just doing some experiments. In the buildout.cfg file, you will find a section called MOAI, it looks like this: .. code-block:: ini [moai] configuration_profile = example extension_modules = moai.extensions The extension_modules line adds modules that will be examined by MOAI to see if it contains extensions. You can add your own modules here, or put some scripts in the default extensions directory. The configuration_profile line determines which profile will be used, you probably want to create your own profile, and change this line. After any changes to the buildout.cfg file, you need to run the buildout script again .. code-block:: sh > bin/buildout Creating your own Configuration Profile --------------------------------------- First, you will need to choose a name for your profile, we will use 'myuni' in this example. Change the configuration_profile value in buildout.cfg .. code-block:: ini configuration_profile = myuni Let's also add some more specific configuration, that our profile will use .. code-block:: ini [myuni] fedora = http://library.myuni.edu/fedora datastream = DATA path = /tmp/myuni database = /tmp/moai Don't forget to run buildout: .. code-block:: sh > bin/buildout To create your own configuration profile, create a python file, and put it in a python module that's listed as an extension module. In the python file, add the following code .. code-block:: python from moai import ConfigurationProfile, name from moai.provider.fedora import FedoraBasedContentProvider from moai.database.btree import BTreeDatabase from moai.update import DatabaseUpdater from moai.server import Server, FeedConfig from moai.http.cherry import start_server from moai.extensions.myuni.content import MyUniContent class MyUniConfiguration(ConfigurationProfile): name('myuni') # configuration profile name used in buildout.cfg def get_content_provider(self): provider = FedoraBasedContentProvider( self.config['fedora'], self.config['path'], self.config['datastream']) provider.set_logger(self.log) return provider def get_database_updater(self): return DatabaseUpdater(self.get_content_provider(), MyUniContent, BTreeDatabase(self.config['database'], 'w'), self.log) def get_database(self): return BTreeDatabase(self.config['database'], 'r') def get_server(self): server = Server('http://localhost:8080/oai', self.get_database()) server.add_config( FeedConfig('publications', 'MyUni Publication Server', 'http://localhost:8080/oai/publications', self.log, sets_allowed=['publication'], metadata_prefixes=['oai_dc', 'mods', 'didl'])) server.add_config( FeedConfig('stuff', 'MyUni Stuff that are Publications Server', 'http://localhost:8080/oai/stuff', self.log, sets_disallowed=['publication'], metadata_prefixes=['oai_dc', 'mods', 'didl'])) return server def start_server(self): start_server('127.0.0.1', 8080, 10, 'oai', self.get_server()) Allthough this might be a bit more complicated then a config file, it does expose all the hooks we're you are able to plug in different implementations making it a very powerful way to customize things. In the released version of MOAI, we will ship with a number of configuration profiles, so you wont have to write a complete configuration profile if it's not needed. Creating your own ContentObjects -------------------------------- A content object will take an object (handed by a ContentProvider) and transform it so it can be inserted into the MOAI database. Since every university will probably use a different metadata format, this object will need to be implemented. In the above configuration profile, a class called MyUniContentObject is imported from a seperate python module. This python module (content.py) could look something like this: .. code-block:: python import os import time from datetime import datetime from lxml import etree from moai.content import XMLContentObject class MyUniContent(XMLContentObject): def update(self, path, provider): self.provider = provider self.nsmap = {'uni':'http://library.myuni.edu/ns'} doc = etree.parse(path) self.root = doc.getroot() self.when_modified = datetime(*time.gmtime(os.path.getmtime(path))[:6]) self.deleted = False self.sets = [] self.content_type = self.root.xpath('local-name()') self.is_set = self.content_type == 'set' self._fields = {} if self.content_type == 'person': self.id = self.xpath('uni:id/text()', 'id', unicode, required=True) self.label = self.xpath('uni:id/text()', 'id', unicode, required=True) else: self.id = self.xpath('uni:uuid/text()', 'id', unicode, required=True) self.label = self.xpath('uni:title/text()', 'id', unicode, required=True) self.sets = self.xpath('uni:oai-sets/text()', 'id', unicode, multi=True) self.sets.append('publication') self._fields = self.add_publication_fields() def add_publication_fields(self): fields = { 'language': self.xpath('uni:language/text()', 'id', unicode, multi=True), 'contributor': self.xpath('uni:creator/text()', 'id', unicode, multi=True), 'title': [self.label], 'publisher': self.xpath('uni:publisher/text()', 'id', unicode, multi=True), 'type': self.xpath('uni:type/text()', 'id', unicode, multi=True), 'subject': self.xpath('uni:type/text()', 'id', unicode, multi=True), } dates = self.xpath('uni:date/text()', 'id', unicode, multi=True) if dates: dateval = parse_date(dates[0]) assert not dateval is None, 'Unknown dateFormat: %s' % dateval fields['date'] = [dateval] fields['author'] = fields['contributor'] fields['url'] = ['http://dx.doi.org/????/%s' % self.id] fields['dare_id'] = ['urn:NBN:nl:ui:??-%s' %self.id] filename = self.xpath('uni:date/text()', 'id', unicode, multi=True) if filename: fields['assets'] = [{'filename': filename[0], 'mimetype': 'application/binary'}] return fields Note that this is just an example of how a content object might look, this will totally depend on your existing content format, and which kind of properties you want to store in the MOAI database. This in turn decides which metadataprefixes you can use in the OAI server.