Dragoman / dragoman / lib /

'''Dragoman Client

This is somewhat modeled after the MongoDB client for Python.

>>> from dragoman.lib.client import Connection
>>> c = Connection('http://localhost:5000/api')
>>> _ =
>>>"I don't like it", "No me gusta lo")
>>> _("I don't like it")
"No me gusta lo"

The goal is to be able to replace the gettext function moreso than to
have a specific client object.
import os

import httplib2
import simplejson

from urllib import quote as urlquote
from urllib import urlencode

from dragoman.lib.urltemplate import Url
from dragoman.lib.httpcache import BsddbCache
from dragoman.lib.gettext_tools import GettextCatalog

class RequestFailure(Exception):
    def __init__(self, msg, resp, content):
        Exception.__init__(self, msg)
        self.resp, self.content = resp, content

class ClientMixin(object):
    def h(self):
        if hasattr(self, '_h'):
            return self._h
        if not hasattr(self, '_cache_name'):
            self._cache_name = '.dragoman_client_cache'

		# thread safe cache using bsddb
        cache = BsddbCache(self._cache_name)
        self._h = httplib2.Http(cache)
        return self._h

    def clear_cache(self):
        # makes sure we have a self._cache_name
        h = self.h

        # clear out the directory
        if os.path.exists(self._cache_name):

class Service(ClientMixin):
    def __init__(self, base):
        self.base = base
        self.urls = self._get_service_doc()

    def _get_service_doc(self):
        resp, content = self.h.request(self.base)
        tmpls = {}
        urls = simplejson.loads(content)
        return dict([(k, Url(v)) for k, v in urls.items()])
    def url(self, key, **params):
        if self.urls.get(key):
            return self.urls[key].replace(_base=self.urls['base'].raw(), **params)

    def request(self, key, urlparams, query=None, *args, **kw):
        url = self.url(key, **urlparams)
        if query:
            url = '%s?%s' % (url, urlencode(kw['query']))
        return self.h.request(url, *args, **kw)
class Language(ClientMixin):
    def __init__(self, catalog, lang, service, cache_name=None):
        self.catalog = catalog
        self.lang = lang
        self.service = service = None

    def _url(self, phrase=None):
        params = {
            'catalog': self.catalog,
            'language': self.lang,
        if phrase:
            params['phrase'] = phrase
            return self.service.url('phrase', **params)
        return self.service.url('catalog', **params)

    def get(self, phrase, **extra):
        query = extra or {}
        urlparams =  { 'catalog': self.catalog,
                       'language': self.lang,
                       'phrase': urlquote(phrase) }
        reqparams = {'method': 'GET'}
        resp, content = self.service.request('phrase', urlparams, query, **reqparams)
        if resp.status == 200 and content:
            return content
        return phrase
    __call__ = get

    def exists(self):
        resp, content = self.service.request('languages', {})
        langs = simplejson.loads(content)
        return self.lang in langs

    def create(self, name):
        reqparams = {'method': 'POST'} = name
        doc = {
            'abbrev': self.lang,
        reqparams = {
            'headers': {
                'Content-Type': 'application/json',
            'method': 'POST',
            'body': simplejson.dumps(doc),
        resp, content = self.service.request('languages', {},  **reqparams)
        if resp.status == 201 and content:
            return content
        raise RequestFailure('Creating language (%s, %s) failed' % (self.lang,, resp, content)

    def add(self, phrase, translation, **extra):
        ctx = extra or {}
        body = {
            'phrase': phrase,
            'translation': translation,
        urlparams =  { 'catalog': self.catalog, 'language': self.lang, 'phrase': urlquote(phrase),}
        reqparams = { 'method': 'POST',
                      'headers': {'Content-Type': 'application/json'},
                      'body': simplejson.dumps(body) }
        resp, content = self.service.request('phrase', urlparams, **reqparams)
        if resp.status == 201:
            return True
        raise RequestFailure('add method failed', resp, content)

    def bulk(self, phrases):
        doc = simplejson.dumps({ self.lang : phrases })
        urlparams =  {'catalog': self.catalog, 'language': self.lang,}

        reqparams = {'method': 'POST',
                     'body': doc,
                     'headers': {'Content-Type': 'application/json'}}
        resp, content = self.service.request('language', urlparams, **reqparams)
        if resp.status == 201:
            return True
        raise RequestFailure('bulk add failed', resp, content)

    def bulk_by_po(self, po_file):
        extra_keys = [
            'extracted_comment', 'reference', 'flag',
        po = GettextCatalog(po_file, self.lang)
        phrases = []
        for m in po.messages:
            extra = [getattr(po, k) for k in extra_keys if hasattr(po, k)]
            extra = dict(zip(extra_keys, extra))
            doc = { 'phrase': m['msgid'], 'translation': m['msgstr'] }
        return self.bulk(phrases)

class Catalog(object):
    def __init__(self, name, service): = name
        self.service = service

    def get(self, lang):
        return Language(, lang, self.service)

    __getattr__ = get
    __getitem__ = get
    __call__ = get

class Connection(object):
    def __init__(self, base):
        if base.endswith('/'):
            base = base[:-1]
        self.base = base
        self.service = Service(self.base)

    def get(self, catalog):
        return Catalog(catalog, self.service)

    __getattr__ = get
    __getitem__ = get
    __call__ = get

if __name__ == '__main__':
    # example
    c = Connection('http://localhost:5000')
    _ =