view ikweb/tools/lazygen_model.py @ 329:c29c8dad0673

added rules for build ports
author "Rex Tsai <chihchun@kalug.linux.org.tw>"
date Thu, 12 Feb 2009 01:24:59 +0800
parents 7747bbe5b68e
children
line wrap: on
line source

#!/usr/bin/env python
import re
import os
import sys

from sqlalchemy import *
# dirty import XD
gaeo_home = os.getenv('GAEO_HOME').replace('~',os.getenv('HOME'))+'/bin'
try:
    sys.path.append(gaeo_home)
    from gaeogen import *
except ImportError, e:
    print "can not import gaeogen, the gaeogen path maybe wrong, got path:%s" % gaeo_home
    sys.exit()


def sqlite_column_mapper(model_class, table_obj):
    """
    map sqlite columns of the table to GAEO Model
    
    >>> metadata = MetaData()
    >>> users_table = Table('users', metadata,
    ...     Column('id', Integer, primary_key=True),
    ...     Column('name', String),
    ...     Column('fullname', String),
    ...     Column('password', String)
    ... )
    >>> model = sqlite_column_mapper(GenModel, users_table)
    >>> model.generate_content()
    """
    def convertor(col_type):
        lowertype = col_type.lower()

        if re.match('char', lowertype): col_type = "String"
        if re.match('(int|integer)', lowertype):    col_type = 'Integer'
        return col_type
    
    model = model_class(table_obj.name)
    
    for column in table_obj.columns:        
        model.add_property( "%s" % GenProperty(column.name, 
                                               column, 
                                               convertor))
    return model

class GenProperty(object):
    """
    GAE Property Generator.
        
    class Property(verbose_name=None, name=None, default=None, required=False, validator=None, choices=None)
    """
    db_prefix = "^(SL|PG|Max|MS|FB|Ac|Info|Oracle)"        

    def __init__(self, name, column=False, convertor=False):
        """
        >>> col = Column('summary', String(2000))
        >>> p = GenProperty('summary', col)
        >>> "%s" % p
        "summary:StringProperty(verbose_name='summary',name='summary')"
        >>> meta = MetaData()
        >>> meta.bind = create_engine("sqlite:///dummy.sqlite")
        >>> t = Table('ally', meta, autoload=True)        
        >>> p = GenProperty('name', t.columns.get('name'))
        >>> "%s" % p
        "name:TextProperty(verbose_name='name',name='name')"
        >>> p = GenProperty('name', t.columns.get('time'))
        >>> "%s" % p
        "name:DatetimeProperty(verbose_name='name',name='name')"
        """
        self.name = name
        self.column = column
        
        # very dirty ...
        if column:
                self.type_name = re.sub( self.db_prefix,'',
                                         column.type.__class__.__name__)
                if convertor:
                    self.type_name = convertor(self.type_name)
        
    def __str__(self):
        return self.gen()
    
    def gen(self):
        """
        generate gaeo property code.
        """
        clause= self.create_clause(self.type_name, self.column) 
        return gae_property_code(self.name, self.type_name, clause)        
    
    def create_clause(self, type, column):
        """
        create property cluase.
                
        >>> s = Column('summary', String(2000))
        >>> code = GenProperty(s.name).create_clause('string', s)
        >>> code
        {'verbose_name': 'summary', 'name': 'summary'}
        """
        clause= self.create_basic_clause(column)
        
        try:
            other_caluse = getattr(self, 'create_%s_clause' % type.lower())(column)
            clause.update(other_caluse)            
        except:
            pass
        
        return clause
    
    def create_basic_clause(self, column):
        """
        create basic property cluase.
        """
        clause = {'verbose_name':self.name, 'name':self.name}
            
        if column.default:
            clause.setdefault('default', column.default)
        elif column.server_default:
            clause.setdefault('default', column.server_default)
            
        return clause

def gae_property_code(name, type, clause):
    """
    generate google app engine property code.
    
    >>> gae_property_code('name', 'string', {'default':'hychen','name':'hi'})
    "name:StringProperty(default='hychen',name='hi')"
    """
    argv = []
    
    def _quote(v):
        try:
            return "'"+v+"'"
        except TypeError:
            pass
    
    for k,v in clause.items():
        argv.append("%s=%s" % (k, _quote(v) ) )

    return "%s:%sProperty(%s)" % (name, 
                                  type.capitalize(),
                                   ','.join(argv))

class ModelMaker:

    is_build = False
    
    def __init__(self, **kwds):
        """
        to create GAEO Model
        
        >>> maker = ModelMaker(schema="sqlite:///dummy.sqlite", autoload=True)
        >>> maker.build()        
        """
        self.models = []
        self.set_engine(kwds['schema'])
        
        try:                        
            if True == kwds['autoload']:
                self.table_names = self.db_engine.table_names()
            else:
                self.table_names = kwds['include_tables']
        except KeyError:
            print "input wrong argv."
        
    def set_engine(self, schema):
        """
        set db engine
        """
        self.db_engine = create_engine(schema)        
        self.meta = MetaData()        
        self.meta.bind = self.db_engine

    def build(self):
        """
        build models by talbse in database.
        """
        mapper_func = '%s_column_mapper' % self.db_engine.name.lower()
        
        for table_name in self.table_names:
            table = Table(table_name, self.meta, autoload=True)            
            self.models.append( eval(mapper_func)( GenModel, table) )
            
        self.is_build = True
            
    def save(self):
        """
        save model
        """
        if self.is_build:
            application_dir = os.path.join(os.getcwd(), 'application')
            model_dir = os.path.join(application_dir, 'model')
                        
            # check if the model directory had been created
            if not os.path.exists(os.path.join(model_dir, '__init__.py')):
                create_file(os.path.join(model_dir, '__init__.py'), [])
                                        
            for model in self.models:
                print 'Creating Model %s ...' % model.name
                model.save(os.path.join(model_dir, '%s.py' % model.name))
        else:
            print "not build yet."

def gen_models_from_db(db_schema, include_tables=False):
    """
    generate models form database.
    
    gen_models_from_db("sqlite://dumy.sqlite")
    
    # spefiy tables
    gen_models_from_db("sqlite://dumy.sqlite", include_tables=['tb1','tb2'])
    """
    if not include_tables:
        maker = ModelMaker(schema=db_schema, autoload=True)
    else:
        maker = ModelMaker(schema=db_schema, include_tables=include_tables)
        
    maker.build()
    maker.save()

if '__main__' == __name__:
    import sys
    
    if 'test' == sys.argv[1]:    
        import doctest
        doctest.testmod()
    else:
        gen_models_from_db(sys.argv[1])