Hello ppl!

I'm trying to create a trac plugin to help on the env's hosted projects I18N 
management.
I have a lot of ideas for it, yet, I'm stuck with the base of it, the database 
layout.
As a general idea, think of it as the rosetta for trac plugin of even the 
pootle for trac plugin.

I send attached the current database layout, which was initially meant to work 
with foreign keys,
yet sqlite does not support that, duh! Either way I thought I should stick with 
that design for
when trac moves to SQLAlchemy ;) which would make my life alot easier :)

Anyway, onto the subject which brought myself to you, the database layout.

The general idea is to import the catalog templates which are on the source 
control system,
this is using babel(of course), populate the database with the catalog 
template, then allow the
users coming to our trac env to create new translations and populate them.
These would of course need to be managed by an admin.

So, we need to store each version of the translation the users provide(per 
msgid), and the admin
then decides which is the best suited. Of course, projects need to get a 
manager for each of the
languages being translated as only one with full knowledge of the language will 
know what is the
best and most suitable translation. But this is yet another step.
As I said, I have the current database layout attached and also the model being 
used. Please, this
is really, really RAW code(and not all of it) since it's the first plug I'm 
coding which relies
on the database.

The idea I have now is:
 * A repo path to a catalog template has a catalog associated to it, which 
itself has the several msgid's.
 * Each msgid has user comments, auto comments, locations, flags, translations, 
etc

I need to handle plurals so, msgid and msgid_plural and then the necessary 
plural translations based on
the language some of them have more than 4 plural forms, way more.

I'd appreciate any comments, improvements or even ppl willing to help on coding 
it, but specially,
if this is/should be the db layout to use.


Best Regards,
-- 
Pedro Algarvio
   __ ___  ___ _______        ___ __
|   Y   .'  _|   _   .-----.'  _|  |_   .-----.----.-----.
|.  |   |   _|   1___|  _  |   _|   _|__|  _  |   _|  _  |
|.  |   |__| |____   |_____|__| |____|__|_____|__| |___  |
|:  1   |    |:  1   |                             |_____|
|::.. . |    |::.. . |  ufs [AT] ufsoft [DOT] org
`-------'    `-------'    ufs [AT] sapo [DOT] pt

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups "Trac 
Development" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/trac-dev?hl=en
-~----------~----~----~----~------~----~------~--~---

from trac.db import Table, Column, Index, DatabaseManager

version = 1

tables = [
    Table('catalog_path', key='id')[
        Column('id', type='int', auto_increment=True),
        Column('path', type='text', unique=True),
        Column('name', type='unicode', unique=True),
        Index(['path', 'name'])
    ],
    Table('catalogs', key=('catalog_path_id', 'revision', 'locale'))[
        Column('catalog_path_id', type='int'),
        Column('revision', type='int'),
        Column('locale'),
        Column('header_comment', type='unicode'),
        Column('copyright_holder', type='unicode'),
        Column('msgid_bugs_address'),
        Column('creation_date', type='timestamp'),
        Column('revision_date', type='timestamp'),
        Column('last_translator', type='unicode'),
        Column('language_team', type='unicode'),
        Column('charset'),
        Column('fuzzy', type='boolean'),
    ],
    Table('catalog_msgids', key=('id'))[
        Column('id', type='int', auto_increment=True),
        Column('catalogs_id', type='int'),
        Column('msgid', type='unicode'),
        Column('msgid_plural', type='unicode'),
        Column('previous_id', type='unicode'),
        Column('previous_id_plural', type='unicode'),
        Column('obsolete', type='boolean'),
        Index(['catalogs_id', 'msgid'])
    ],
    Table('catalog_msgstrs', key=('catalog_msgid_id', 'idx', 'string'))[
        Column('catalog_msgid_id', type='int'),
        Column('idx', type='int'),
        Column('string', type='unicode'),
        Column('sid'),
        Column('accepted', type='boolean'),
        Index(['catalog_msgid_id', 'idx', 'string'])
    ],
    Table('catalog_msgid_locations', key=('catalog_msgid_id', 'location',
                                          'lineno'))[
        Column('catalog_msgid_id', type='int'),
        Column('location'),
        Column('lineno', type='int'),
    ],
    Table('catalog_message_auto_comments', key='catalog_msgid_id')[
        Column('catalog_msgid_id', type='int'),
        Column('auto_comment', type='unicode'),
    ],
    Table('catalog_msgid_user_comments', key='catalog_msgid_id')[
        Column('catalog_msgid_id', type='int'),
        Column('user_comment', type='unicode'),
    ],
    Table('catalog_msgid_flags', key='catalog_msgid_id')[
        Column('catalog_msgid_id', type='int'),
        Column('flag'),
    ],
]

def do_upgrade(env, ver, cursor):
    db_connector, _ = DatabaseManager(env)._get_connector()
    for table in tables:
        for stmt in db_connector.to_sql(table):
            try:
                cursor.execute(stmt)
            except Exception, err:
                print stmt
                raise err
# -*- coding: utf-8 -*-
# vim: sw=4 ts=4 fenc=utf-8
# =============================================================================
# $Id$
# =============================================================================
#             $URL$
# $LastChangedDate$
#             $Rev$
#   $LastChangedBy$
# =============================================================================
# Copyright (C) 2007 UfSoft.org - Pedro Algarvio <[EMAIL PROTECTED]>
#
# Please view LICENSE for additional licensing information.
# =============================================================================

from UserDict import DictMixin
from babel.messages.pofile import read_po
from trac.util.datefmt import to_timestamp
from cStringIO import StringIO

class BaseDBClass(object):
    def __init__(self, env):
        self.env = env
        self.log = env.log
        self.db = self.env.get_db_cnx()
        print "%s : inittialized" % self.__class__.__name__

    def save(self):
        raise NotImplementedError

    def refresh(self):
        raise NotImplementedError

class CatalogPath(BaseDBClass):
    id = None
    path = _path = None
    name = _name = False


    def __init__(self, env, name, path=None):
        BaseDBClass.__init__(self, env)
        self.path = path
        self.name = name
        self.refresh()
        print 'name: %s  path:%s' % (name, path)


    def refresh(self):
        print 'refreshing'
        cursor = self.db.cursor()
        cursor.execute('SELECT id, path, name from catalog_path '
                       'WHERE (name=%s AND path=%s) OR name=%s',
                       (self.name, self.path, self.name))
        row = cursor.fetchone()
        print 'fetched'
        if row:
            print 'path row', row
            id, path, name = row
            self.id = id
            self.path = self._path = path
            self.name = self._name = name

    def save(self):
        print 'saving'
        cursor = self.db.cursor()
        cursor.execute("UPDATE catalog_path SET path=%s, name=%s WHERE "
                       "id=%s AND path=%s AND name=%s",
                       (self.path, self.name,
                        self.id, self._path, self._name))
        if not cursor.rowcount:
            print 'saving new'
            cursor.execute("INSERT INTO catalog_path (path, name) "
                           "VALUES (%s, %s)",
                           (self.path, self.name))

        self.db.commit()
        self.refresh()

    def delete(self):
        print 'Deleting: %s, %s, %s' % (self.id, self.path, self.name)
        cursor = self.db.cursor()
        cursor.execute('DELETE FROM catalog_path WHERE name=%s AND id=%s',
                       (self.name, self.id))
        self.db.commit()

class Catalog(BaseDBClass):
    catalog_path_id = None
    header_comment = None
    copyright_holder = None
    msgid_bugs_address = None
    creation_date = None
    revision_date = None
    language_team = None
    last_translator = None
    charset = None
    fuzzy = None
    content = None

    def __init__(self, env, catalog_path, revision, content=None, locale=None):
        BaseDBClass.__init__(self, env)
        assert isinstance(catalog_path, CatalogPath)
        self.catalog_path = catalog_path
        self.catalog_path_id = catalog_path.id
        self.revision = revision
        self.locale = locale
        if content:
            self.content = StringIO(content.read())
        self.refresh()
#        setattr(self, 'messages', lambda x: self.get_messages())
#        self.messages = self.get_messages()
        print 'catalog inited'

    def refresh(self):
        cursor = self.db.cursor()
        cursor.execute('SELECT catalog_path_id, locale, header_comment, '
                       'copyright_holder, msgid_bugs_address, '
                       #'creation_date, revision_date, '
                       'last_translator, language_team, '
                       'charset, fuzzy FROM catalogs WHERE catalog_path_id=%s '
                       'AND revision=%s',
                       (self.catalog_path_id, self.revision,))
        print 'select executed'
        row = cursor.fetchone()
        if row:
            print 'we have row'
            catalog_path_id, locale, header_comment, copyright_holder, msgid_bugs_address, \
            last_translator, language_team, \
            charset, fuzzy = row
            self.catalog_path_id = catalog_path_id
            self.locale = locale
            self.header_comment = header_comment
            self.copyright_holder = copyright_holder
            self.msgid_bugs_address = msgid_bugs_address
            #self.creation_date = creation_date
            #self.revision_date = revision_date
            self.last_translator = last_translator
            self.language_team = language_team
            self.charset = charset
            self.fuzzy = fuzzy
        else:
            print 'calling parse'
            self.parse()

    def parse(self):
        if not self.content:
            return
        catalog = read_po(self.content)
        self.msgid_bugs_address = catalog.msgid_bugs_address
        self.copyright_holder = catalog.copyright_holder
        self.charset = catalog.charset
        #self.creation_date = catalog.creation_date
        #self.revision_date = catalog.revision_date
        self.language_team = catalog.language_team
        self.last_translator = catalog.last_translator
        self.header_comment = catalog.header_comment
        self.fuzzy = catalog.fuzzy
        print 'importing messages'
#        print 'catalog', catalog
#        messages =
#        print 'messages', messages
        messages = list(catalog)[1:]
        #print 'len', len(messages)
        for msg in messages:
            print 11, msg.__dict__
            message = MsgID(self, msg.id, msg.previous_id)
            message.save()
            string = []
            if isinstance(msg.string, basestring):
                string = [msg.string]

            for idx, string in enumerate(string):
                if string:
                    translation = MsgSTR(message, idx, string)
                    translation.save()

            for entry in msg.locations:
                location, lineno = entry
                msgid_location = Location(message, location, lineno)
                msgid_location.save()

    def save(self):
        cursor = self.db.cursor()
        cursor.execute('UPDATE catalogs SET catalog_path_id=%s, revision=%s, '
                       'locale=%s, header_comment=%s, '
                       'copyright_holder=%s, msgid_bugs_address=%s, '
                       'creation_date=%s, revision_date=%s, '
                       'last_translator=%s, language_team=%s, charset=%s, '
                       'fuzzy=%s',
                       (self.catalog_path_id, self.revision,
                        self.locale, self.header_comment,
                        self.copyright_holder, self.msgid_bugs_address,
                        self.creation_date, self.revision_date,
                        self.last_translator, self.language_team,
                        self.charset, self.fuzzy))
        if not cursor.rowcount:
            print 'entry not in db'
            cursor.execute('INSERT INTO catalogs (catalog_path_id, revision, '
                           'locale, header_comment, copyright_holder, '
                           'msgid_bugs_address, creation_date, revision_date, '
                           'last_translator, language_team, charset, fuzzy) '
                           'VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, '
                           '%s, %s)',
                           (self.catalog_path_id, self.revision, self.locale,
                            self.header_comment, self.copyright_holder,
                            self.msgid_bugs_address, self.creation_date,
                            self.revision_date, self.last_translator,
                            self.language_team, self.charset, self.fuzzy))
        self.db.commit()

    def get_messages(self):
        print 'fetching messages by property for catalog_path_id: %s' % self.catalog_path_id
        messages = []
        cursor = self.db.cursor()
        cursor.execute('SELECT * from catalog_msgids WHERE catalogs_id=%s',
                       (self.catalog_path_id,))
        rows = cursor.fetchall()
        for row in rows:
            id, catalogs_id, msgid, msgid_plural, previous_id, \
            previous_id_plural, obsolete = row
            msg = MsgID(self,
                        (msgid, msgid_plural),
                        (previous_id, previous_id_plural))
            #print 'locations', msg.locations
            messages.append(msg)
#        self.messages = messages
        return messages
    messages = property(get_messages)

class MsgID(BaseDBClass):
    id = None
    catalogs_id = None
    msgid = _msgid = None
    msgid_plural = _msgid_plural = None
    previous_id = _previous_id = None
    previous_id_plural = _previous_id_plural = None
    obsolete = _obsolete = False
    def __init__(self, db_catalog, msgid, previous_id):
        assert isinstance(db_catalog, Catalog)
        BaseDBClass.__init__(self, db_catalog.env)
        self.db_catalog = db_catalog
        self.catalogs_id = db_catalog.catalog_path_id
        if isinstance(msgid, (list, tuple)) and len(msgid) > 1:
            self.msgid, self.msgid_plural = msgid
        else:
            self.msgid = msgid
        if previous_id and len(previous_id) > 1:
            self.previous_id, self.previous_id_plural = previous_id
        else:
            self.previous_id = previous_id or ''
        self.refresh()

    def refresh(self):
        print 'looking up msgid on db'
        cursor = self.db.cursor()
        print 'got cursor'
        print '000', self.catalogs_id, self.msgid, self.msgid_plural, \
        self.previous_id, self.previous_id_plural, self.obsolete
        try:
            cursor.execute('SELECT * FROM catalog_msgids WHERE '
                           'catalogs_id=%s AND msgid=%s',
                           (self.catalogs_id, self.msgid))
        except Exception, err:
            print 'ERR', err
        print 'select done'
        row = cursor.fetchone()
        if row:
            print 'got row'
            id, catalog_id, msgid, msgid_plural, previous_id, \
            previous_id_plural, obsolete = row
            self.id = id
            self.msgid = msgid
            self.msgid_plural = msgid_plural
            self.previous_id = previous_id
            self.previous_id_plural = previous_id_plural
            self.obsolete = obsolete

    def save(self):
        print 'saving msgid'
        try:
            cursor = self.db.cursor()
            cursor.execute('UPDATE catalog_msgids SET catalogs_id=%s, msgid=%s, '
                           'msgid_plural=%s, previous_id=%s, '
                           'previous_id_plural=%s, obsolete=%s WHERE '
                           'catalogs_id=%s AND msgid=%s',
                           (self.catalogs_id, self.msgid, self.msgid_plural,
                            self.previous_id, self.previous_id_plural,
                            self.obsolete, self.catalogs_id, self.msgid))
            if not cursor.rowcount:
                print 'creating new msgid'
                cursor.execute('INSERT INTO catalog_msgids (catalogs_id, msgid, '
                               'msgid_plural, previous_id, previous_id_plural, '
                               'obsolete) VALUES (%s, %s, %s, %s, %s, %s)',
                               (self.catalogs_id, self.msgid, self.msgid_plural,
                                self.previous_id, self.previous_id_plural,
                                self.obsolete))
            self.db.commit()
        except Exception, err:
            print err
            raise err
        self.refresh()

    def get_locations(self):
        #print 'getting locations for msgid.id: %s' % self.id
        locations = []
        cursor = self.db.cursor()
        cursor.execute('SELECT * FROM catalog_msgid_locations WHERE '
                       'catalog_msgid_id=%s',
                       (self.id,))
        rows = cursor.fetchall()
        for row in rows:
            #print 'location row for msgid_id %s' % self.id, row
            msgid_id, loc, lineno = row
            location = Location(self, loc, lineno)
            locations.append(location)
        return locations
    locations = property(get_locations)


class MsgIDBaseClass(BaseDBClass):
    def __init__(self, db_msgid):
        assert isinstance(db_msgid, MsgID)
        BaseDBClass.__init__(self, db_msgid.env)
        self.msgid_id = db_msgid.id


class MsgSTR(MsgIDBaseClass):
    idx = _idx = None
    string = _string = None
    def __init__(self, db_msgid, idx, string):
        MsgIDBaseClass.__init__(self, db_msgid)
        self.idx = self._idx = idx
        self.string = self._string = string


    def refresh(self):
        cursor = self.db.cursor()
        cursor.execute('SELECT * from catalog_msgstrs WHERE catalog_msgid_id=%s'
                       'AND idx=%s AND string=%s',
                       (self.msgid_id, self._idx or self.idx,
                        self._string or self.string))
        row = cursor.fetchone()
        if row:
            msgid_id, idx, string = row
            self.msgid_id = msgid_id
            self.idx = idx
            self.string = string

    def save(self):
        print "%s: Updating msgid_id: %s  ix: %s  string: %s" % (
            self.__class__.__name__, self.msgid_id, self.idx, self.string)
        cursor = self.db.cursor()
        cursor.execute('UPDATE catalog_msgstrs SET catalog_msgid_id=%s, '
                       'idx=%s, string=%s WHERE catalog_msgid_id=%s AND '
                       'idx=%s AND string=%s',
                       (self.msgid_id, self.idx, self.string,
                        self.msgid_id, self._idx, self._string))
        if not cursor.rowcount:
            print "%s: Not found, creating new entry" % self.__class__.__name__
            cursor.execute('INSERT INTO catalog_msgstrs (catalog_msgid_id, '
                           'idx, string) VALUES (%s, %s, %s)',
                           (self.msgid_id, self.idx, self.string))

        self.db.commit()
        self.refresh()



class Location(MsgIDBaseClass):
    location = _location = None
    lineno = _lineno = None
    def __init__(self, db_msgid, location, lineno):
        MsgIDBaseClass.__init__(self, db_msgid)
        self.location = location
        self.lineno = lineno
        self.refresh()

    def refresh(self):
        cursor = self.db.cursor()
        cursor.execute('SELECT * from catalog_msgid_locations WHERE '
                       'catalog_msgid_id=%s AND location=%s AND lineno=%s',
                       (self.msgid_id, self.location, self.lineno))
        row = cursor.fetchone()
        if row:
            msgid_id, location, lineno = row
            self.msgid_id = msgid_id
            self.location = self._location = location
            self.lineno = self._lineno = lineno

    def save(self):
        cursor = self.db.cursor()
        cursor.execute('UPDATE catalog_msgid_locations SET catalog_msgid_id=%s,'
                       ' location=%s, lineno=%s WHERE catalog_msgid_id=%s AND '
                       'location=%s AND lineno=%s',
                       (self.msgid_id, self.location, self.lineno,
                        self.msgid_id, self._location, self._lineno))
        if not cursor.rowcount:
            cursor.execute('INSERT INTO catalog_msgid_locations ('
                           'catalog_msgid_id, location, lineno) VALUES '
                           '(%s, %s, %s)',
                           (self.msgid_id, self.location, self.lineno))
        self.db.commit()
        self.refresh()

Reply via email to