Package Camelot :: Package camelot :: Package bin :: Module camelot_manage |
[frames] | no frames]

# Source Code for Module Camelot.camelot.bin.camelot_manage

      1  #  ============================================================================

      2  #

      3  #  Copyright (C) 2007-2008 Conceptive Engineering bvba. All rights reserved.

      4  #  www.conceptive.be / project-camelot@conceptive.be

      5  #

      6  #  This file is part of the Camelot Library.

      7  #

      8  #  This file may be used under the terms of the GNU General Public

      9  #  License version 2.0 as published by the Free Software Foundation

     10  #  and appearing in the file LICENSE.GPL included in the packaging of

     11  #  this file.  Please review the following information to ensure GNU

     12  #  General Public Licensing requirements will be met:

     13  #  http://www.trolltech.com/products/qt/opensource.html

     14  #

     15  #  If you are unsure which license is appropriate for your use, please

     16  #  review the following information:

     17  #  http://www.trolltech.com/products/qt/licensing.html or contact

     18  #  project-camelot@conceptive.be.

     19  #

     20  #  This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE

     21  #  WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.

     22  #

     23  #  For use of this library in commercial applications, please contact

     24  #  project-camelot@conceptive.be

     25  #

     26  #  ============================================================================

     27

     28  from optparse import OptionParser
     29  from code import InteractiveConsole
     30  import sys
     31

     32  #

     33  # Description of the application, out of which the help text as well as the

     34  # __doc__ strings can be generated

     35  #

     36

     37  usage = "usage: %prog [options] command"
     38

     39  description = """

     40  camelot_manage is oriented towards administrators of an installed

     41  camelot application. It is used for interacting the database, the model

     42  and migration of the database to a certain schema revision.

     43

     44  To use this application, PYTHONPATH should contain a valid settings.py file that

     45  will be used to resolve the database engine and the model.

     46  """
     47

     48  command_description = [

     49      ('console', """Launches a python console with the model all setup for command line

     50  interaction.

     51

     52  Within the example movie project one could do

     53  the following to print a list of all movie titles to the screen::

     54

     55      from model import Movie

     56      for movie in Movie.query.all():

     57      print movie.title

     58  """),

     59      ('db_version', """Get the version of the database schema from the current database"""),

     60

     61      ('version', """Get the latest available database schema version"""),

     62

     63      ('upgrade', """Upgrade or downgrade the database to the specified version, use upgrade version_number."""),

     64

     65      ('version_control', """Put the database under version control"""),

     66

     67      ('schema_display', """Generate a graph of the database schema.  The result is stored in schema.png.  This

     68  option requires pydot to be installed."""),

     69  ]
     70

     71  #

     72  # Generate a docstring in restructured text format

     73  #

     74

     75  __doc__ = description
     76

     77  for command, desc in command_description:
     78      __doc__ += "\n.. cmdoption:: %s\n\n"%command
     79      for line in desc.split('\n'):
     80          __doc__ += "    %s\n"%line
     81

     82  __doc__ += """

     83     .. image:: ../_static/schema.png

     84        :width: 400"""
     85

     86  #

     87  # A custom OptionParser that generates help information on the commands

     88  #

     89 -class CommandOptionParser(OptionParser):
     90

     91 -    def format_help(self, formatter=None):
     92          command_help = """

     93  The available commands are :

     94

     95  """
     96          command_help += '\n\n'.join(['%s\n%s\n%s'%(command,'-'*len(command), desc) for command,desc in command_description])
     97          command_help += """

     98

     99  For the creation and development of Camelot projects, see camelot_admin

    100

    101  """
    102          return OptionParser.format_help(self) + ''.join(command_help)
    103

    104 -class FileCacher:
    105      "Cache the stdout text so we can analyze it before returning it"
    106 -    def __init__(self): self.reset()
    107 -    def reset(self): self.out = []
    108 -    def write(self,line): self.out.append(line)
    109 -    def flush(self):
    110          output = '\n'.join(self.out)
    111          self.reset()
    112          return output
    113

    114 -class Shell(InteractiveConsole):
    115      "Wrapper around Python that can filter input/output to the shell"
    116 -    def __init__(self, locals={}):
    117          self.stdout = sys.stdout
    118          self.cache = FileCacher()
    119          InteractiveConsole.__init__(self, locals)
    120          return
    121 -    def get_output(self): sys.stdout = self.cache
    122 -    def return_output(self): sys.stdout = self.stdout
    123 -    def push(self,line):
    124          line = line.replace('\r','')
    125          self.get_output()
    126          # you can filter input here by doing something like

    127          # line = filter(line)

    128          InteractiveConsole.push(self,line)
    129          self.return_output()
    130          output = self.cache.flush()
    131          # you can filter the output here by doing something like

    132          # output = filter(output)

    133          print output # or do something else with it
    134          return
    135

    136 -def schema_display(image_path='schema.png'):
    137      from camelot.core.schema_display import create_uml_graph
    138      from sqlalchemy import orm
    139      from elixir import entities
    140      mappers = [orm.class_mapper(e) for e in entities]
    141      graph = create_uml_graph(mappers,

    142          show_operations=False, # not necessary in this case

    143          show_multiplicity_one=False # some people like to see the ones, some don't

    144      )
    145      graph.write_png(image_path)
    146

    147 -def setup_model():
    148      import settings
    149      settings.setup_model()
    150

    151 -def main():
    152      import camelot
    153      parser = CommandOptionParser(usage=usage,

    154                                   description=description,

    155                                   version=camelot.__version__)
    156      (_options, args) = parser.parse_args()
    157      if not args:
    158          parser.print_help()
    159      elif args[0]=='console':
    160          setup_model()
    161          sh = Shell()
    162          sh.interact()
    163      elif args[0]=='schema_display':
    164          setup_model()
    165          schema_display()
    166      elif args[0] in ('version_control', 'db_version', 'version', 'upgrade'):
    167          import settings
    168          from migrate.versioning.repository import Repository
    169          from migrate.versioning.schema import ControlledSchema
    170          from sqlalchemy.exceptions import NoSuchTableError
    171          migrate_engine = settings.ENGINE()
    172          repository = Repository(settings.REPOSITORY)
    173          schema = None
    174          if args[0]=='version_control':
    175              migrate_connection = migrate_engine.connect()
    176              transaction = migrate_connection.begin()
    177              try:
    178                  schema = ControlledSchema.create(migrate_engine, repository)
    179                  transaction.commit()
    180              except:
    181                  transaction.rollback()
    182                  raise
    183              finally:
    184                  migrate_connection.close()
    185              print 'database was put under version control'
    186          try:
    187              schema = ControlledSchema(migrate_engine, repository)
    188          except NoSuchTableError:
    189              print 'database not yet under version control, use the version_control command first.'
    190          if schema:
    191              if args[0]=='db_version':
    192                  print schema.version
    193              elif args[0]=='version':
    194                  print repository.latest
    195              elif args[0]=='upgrade':
    196                  migrate_connection = migrate_engine.connect()
    197                  if len(args)>=2:
    198                      version = int(args[1])
    199                  else:
    200                      version = repository.latest
    201                  #

    202                  # perform each upgrade step in a separate transaction, since

    203                  # one upgrade might depend on an other being fully executed

    204                  #

    205                  try:
    206                      if schema.version == version:
    207                          print 'database is allready at requested version'
    208                      if schema.version <= version:
    209                          step = 1
    210                      else:
    211                          step = -1
    212                      for i in range(schema.version+step, version+step, step):
    213                          transaction = migrate_connection.begin()
    214                          try:
    215                              schema.upgrade(i)
    216                              transaction.commit()
    217                              if step==1:
    218                                  print 'upgrade %s'%i
    219                              else:
    220                                  print 'downgrade %s'%i
    221                          except:
    222                              transaction.rollback()
    223                              raise
    224                  finally:
    225                      migrate_connection.close()
    226      else:
    227          parser.print_help()
    228

    229  if __name__ == '__main__':
    230      main()
    231