Package turbogears :: Package toolbox :: Package designer

Source Code for Package turbogears.toolbox.designer

  1  """Graphical user interface for SQLObject's model design""" 
  2   
  3  import os 
  4  import string 
  5  import shutil 
  6   
  7  import pkg_resources 
  8   
  9  import cherrypy 
 10   
 11  try: 
 12      import sqlobject 
 13  except ImportError: 
 14      sqlobject = None 
 15   
 16  import turbogears 
 17  from turbogears import controllers, expose 
 18  from turbogears.util import get_model, get_package_name, get_project_name 
 19   
 20   
 21  model_template = """from datetime import datetime 
 22  from turbogears.database import PackageHub 
 23  from sqlobject import * 
 24  from turbogears import identity 
 25   
 26  hub = PackageHub('%s') 
 27  __connection__ = hub 
 28   
 29  %s 
 30  """ 
 31   
 32  session_file_name = 'model_designer.tmp' 
33 34 35 -def version_file(file_spec, vtype='copy'):
36 """By Robin Parmar & Martin Miller from Python Cookbook 2. edition""" 37 38 if os.path.isfile(file_spec): 39 # check the 'vtype' parameter 40 if vtype not in ('copy', 'rename'): 41 raise ValueError('Unknown vtype %r' % (vtype,)) 42 43 # determine root file name so the extension doesn't get longer and longer... 44 n, e = os.path.splitext(file_spec) 45 46 # is e a three-digets integer preceded by a dot? 47 if len(e) == 4 and e[1:].isdigit(): 48 num = 1 + int(e[1:]) 49 root = n 50 else: 51 num = 0 52 root = file_spec 53 54 # Find next available file version 55 for i in xrange(num, 1000): 56 new_file = '%s.%03d' % (root, i) 57 if not os.path.exists(new_file): 58 if vtype == 'copy': 59 shutil.copy(file_spec, new_file) 60 else: 61 os.rename(file_spec, new_file) 62 return True 63 raise RuntimeError("Can't %s %r, all names taken" % (vtype, file_spec)) 64 return False
65
66 67 -class Designer(controllers.RootController):
68 """Designer for SQLObject models. 69 70 Create your classes, define your fields and manage your relations. 71 Visualize and generate code for SQLObject models. 72 73 """ 74 75 __label__ = 'ModelDesigner' 76 __version__ = '0.1' 77 __author__ = 'Ronald Jaramillo' 78 __email__ = 'ronald@checkandshare.com' 79 __copyright__ = 'Copyright 2005 Ronald Jaramillo' 80 __license__ = 'MIT' 81 82 baseTemplate = 'turbogears.toolbox.designer' 83 model = None 84 icon = "/tg_static/images/designer.png" 85
86 - def __init__(self):
87 if not sqlobject: 88 raise ImportError("Cannot run the Model Designer.\n" 89 "The SQLObject package is not installed.") 90 self.register_static_directory() 91 self.model = self.get_model_name()
92
93 - def set_model(self, model):
94 self.model = model
95
96 - def get_model_name(self):
97 if not get_project_name(): 98 return False 99 return get_model()
100
102 static_directory = pkg_resources.resource_filename(__name__, 'static') 103 turbogears.config.update({'/tg_toolbox/designer': { 104 'tools.staticdir.on' : True, 105 'tools.staticdir.dir': static_directory}})
106
107 - def column_type(self, column):
108 """Given a column representation return the column type.""" 109 column_type = '%r' % column 110 return column_type.split()[0][1:].split('.')[-1]
111
112 - def column_default(self, column, column_type):
113 try: 114 default = column.default 115 except AttributeError: 116 return '' 117 if default == sqlobject.sqlbuilder.NoDefault: 118 return '' 119 if column_type in ('SOIntCol', 'SOFloatCol', 'SOStringCol', 120 'SODecimalCol', 'SOCurrencyCol'): 121 return default 122 elif column_type == 'SODateTimeCol': 123 d = '%s' % default 124 return ':'.join(d.split(':')[:-1]) 125 elif column_type == 'SODateCol': 126 d = '%s' % default 127 return d.split(None, 1)[0] 128 return ''
129
130 - def load_column(self, column_name, column, model_object):
131 props = {} 132 props['type'] = self.column_type(column) 133 props['column_name'] = column_name 134 props['name'] = column_name 135 props['column_default'] = self.column_default(column, props['type']) 136 try: 137 props['title'] = getattr(column, 'title') or '' 138 except AttributeError: 139 props['title'] = '' 140 if props['type'] == 'SOStringCol': 141 props['length'] = column.length 142 props['varchar'] = column.varchar 143 if props['type'] == 'SOEnumCol': 144 props['enum_values'] = column.enumValues 145 if props['type'] == 'SOForeignKey': 146 props['join_type'] = 'MultipleJoin' 147 props['other_class_name'] = column.foreignKey 148 props['other_method_name'] = self.other_method_name( 149 column, model_object.__name__) 150 if props['type'] in ('SOMultipleJoin','SORelatedJoin'): 151 props['other_class_name'] = column.otherClassName 152 props['join_type'] = props['type'].replace('SO', '') 153 props['other_method_name'] = self.other_method_join_name( 154 column, model_object.__name__) 155 156 props['type'] = props['type'].replace('SO','') 157 return props
158
159 - def other_method_join_name(self, column, model_object_name):
160 for col in column.otherClass.sqlmeta.columnList: 161 if type(col) == sqlobject.SOForeignKey: 162 if col.foreignKey == model_object_name: 163 return col.origName 164 return 'id'
165
166 - def other_method_name(self, column, model_object_name):
167 other_class_name = column.foreignKey 168 model_object = getattr(self.get_model_name(), other_class_name) 169 for col in model_object.sqlmeta.joins: 170 if col.otherClassName == model_object_name: 171 return col.joinMethodName 172 return 'id'
173
174 - def is_inheritable_base_class(self, obj):
175 """Check if the object is a direct subclass of InheritableSQLObject""" 176 return 'sqlobject.inheritance.InheritableSQLObject' in str(obj.__bases__)
177
178 - def load_columns(self, model_object):
179 columns = {} 180 # get normal columns 181 for column_name in model_object.sqlmeta.columns: 182 column = model_object.sqlmeta.columns[column_name] 183 origname = column.origName 184 if model_object._inheritable and column_name == 'childName': 185 continue 186 columns[origname] = self.load_column(origname, column, model_object) 187 # get join columns 188 for column in model_object.sqlmeta.joins: 189 columns[column.joinMethodName] = self.load_column( 190 column.joinMethodName, column, model_object) 191 return columns
192 193 @expose('json')
194 - def load_current_model(self):
195 model = self.get_model_name() 196 current_model = {} 197 current_model['name'] = get_package_name() 198 current_model['models'] = {} 199 current_model['ordered_models'] = [] 200 201 for m in dir(model): 202 if m in ('SQLObject', 'InheritableSQLObject'): 203 continue 204 model_object = getattr(model, m) 205 if isinstance(model_object, type) and issubclass( 206 model_object, sqlobject.SQLObject): 207 parent_class = 'SQLObject' 208 if model_object._inheritable: 209 parent_class = model_object._parentClass.__name__ 210 columns = self.load_columns(model_object) 211 current_model['ordered_models'].append(m) 212 current_model['models'][m] = { 213 'name': m, 214 'parent_class': parent_class, 215 'table_name': model_object.sqlmeta.table, 216 'id_name': model_object.sqlmeta.idName, 217 'columns': columns, 218 'relations': {}} 219 return dict(model=current_model)
220
221 - def save_session_as_name(self, name):
222 # remove non-ascii 223 if isinstance(name, unicode): 224 name = name.encode('ascii', 'replace') 225 # remove punctuation 226 name = name.translate(string.maketrans('', ''), string.punctuation) 227 # camelcase to remove spaces 228 name = ''.join([x.title() for x in name.split()]) 229 sessions_directory = pkg_resources.resource_filename(__name__, 230 os.path.join('static', 'sessions')) 231 for idx in range(100): 232 postfix = idx and '_%d' % idx or '' 233 test_name = '%s%s.js' % (name, postfix) 234 full_name = os.path.join(sessions_directory, test_name) 235 if not os.path.exists(full_name): 236 return full_name
237 238 @expose('json')
239 - def save_state(self, state, name=session_file_name):
240 if name != session_file_name: 241 name = self.save_session_as_name(name) 242 if not name: 243 return dict(state=False) 244 f = open(name,'w') 245 f.write(state) 246 f.close() 247 return dict(state=True)
248
249 - def model_path(self):
250 return os.path.abspath( 251 os.path.join(get_package_name(), 'model.py'))
252
253 - def save_model(self, code):
254 project_name = get_package_name() 255 model_text = model_template % (project_name, code) 256 model_path = self.model_path() 257 if os.path.exists(model_path): 258 version_file(model_path) 259 f = open(model_path,'w') 260 f.write(model_text) 261 f.close() 262 return True, 'Saved %s' % model_path 263 else: 264 return False, 'Failed to save %s' % model_path
265
266 - def save_and_create_(self, code, order):
267 model_path = os.path.join(get_project_name(), 'tmp2_model.py') 268 model_path = os.path.abspath(model_path) 269 open(model_path, 'w').write(str) 270 package = __import__(get_package_name(), {}, {}, ['tmp2_model']) 271 package.tmp2_model.Persona.createTable(ifNotExists=True) 272 model = self.get_model_name() 273 model.Persona = package.tmp2_model.Persona 274 # pk = package.model 275 # import tmp_model as pk 276 # pk.Persona.createTable(ifNotExists=True) 277 return dict(status='Tables ok')
278 279 @expose('json')
280 - def save_and_create(self, code, order):
281 status_code, status = self.save_model(code) 282 if not status_code: 283 return dict(status=status) 284 tmp_name = 'tmp_model' 285 286 # copy the model file 287 model_path = self.model_path() 288 tmp_model_path = model_path.replace('model.py','%s.py' % tmp_name) 289 try: 290 shutil.copy(model_path, tmp_model_path) 291 except IOError, e: 292 return dict(status='Failed to create a temporary model file: %s' % e) 293 294 model = self.get_model_name() 295 package = __import__(get_package_name(), {}, {}, [tmp_name]) 296 tmp_model = getattr(package, tmp_name) 297 298 ok = [] 299 fail = [] 300 classes = order.split(',') 301 for class_name in classes: 302 try: 303 obj = getattr(tmp_model, class_name) 304 obj.createTable(ifNotExists=True) 305 setattr(model, class_name, obj) 306 ok.append('Table created for class %s' % class_name) 307 except Exception, e: 308 fail.append('Failed to create table for class %s: %s' 309 % (class_name,e)) 310 311 if len(ok): 312 status += '\nTables Created: \n%s' % '\n'.join(ok) 313 if len(fail): 314 status += '\nFailed to create Tables:\n%s' % '\n'.join(fail) 315 316 try: 317 os.remove(tmp_model_path) 318 except IOError, e: 319 print "Fail to remove temporary model file: %s" % e 320 321 return dict(status=status)
322
323 - def load_session_list(self):
324 sessions_directory = pkg_resources.resource_filename( 325 __name__, os.path.join('static', 'sessions')) 326 return [x.replace('.js','') 327 for x in os.listdir(sessions_directory) if x.endswith('.js')]
328
329 - def session_exists(self):
330 return os.path.exists(session_file_name)
331 332 @expose('json')
333 - def retrieve_sample(self, name):
334 sessions_directory = pkg_resources.resource_filename( 335 __name__, os.path.join('static', 'sessions')) 336 full_path = os.path.join(sessions_directory,'%s.js' % name) 337 if not os.path.exists(full_path): 338 return dict() 339 return dict(session=open(full_path,'r').read())
340 341 @expose('json')
342 - def current_session_model(self):
343 if not self.session_exists(): 344 return dict() 345 return dict(session=open(session_file_name, 'r').read())
346 347 @expose('json')
348 - def save(self, code):
349 status = self.save_model(code)[1] 350 return dict(status=status)
351 352 @expose('json')
353 - def session_list(self):
354 return dict(models=self.load_session_list())
355 356 @expose('%s.modelDesigner' % baseTemplate)
357 - def index(self):
358 return dict(model_exists=bool(self.model), 359 session_file_exists=self.session_exists(), 360 session_list=self.load_session_list(), 361 model_name=get_project_name())
362