devtools/devctl.py
branchstable
changeset 6356 e89f71a41e20
parent 6138 65f5e488f983
child 6443 a5bed0cd3956
child 6749 48f468f33704
equal deleted inserted replaced
6355:b4ca544ceff0 6356:e89f71a41e20
    24 # *ctl module should limit the number of import to be imported as quickly as
    24 # *ctl module should limit the number of import to be imported as quickly as
    25 # possible (for cubicweb-ctl reactivity, necessary for instance for usable bash
    25 # possible (for cubicweb-ctl reactivity, necessary for instance for usable bash
    26 # completion). So import locally in command helpers.
    26 # completion). So import locally in command helpers.
    27 import sys
    27 import sys
    28 from datetime import datetime
    28 from datetime import datetime
    29 from os import mkdir, chdir
    29 from os import mkdir, chdir, listdir, path as osp
    30 from os.path import join, exists, abspath, basename, normpath, split, isdir
       
    31 from warnings import warn
    30 from warnings import warn
    32 
    31 
    33 from logilab.common import STD_BLACKLIST
    32 from logilab.common import STD_BLACKLIST
    34 
    33 
    35 from cubicweb.__pkginfo__ import version as cubicwebversion
    34 from cubicweb.__pkginfo__ import version as cubicwebversion
    36 from cubicweb import CW_SOFTWARE_ROOT as BASEDIR, BadCommandUsage
    35 from cubicweb import CW_SOFTWARE_ROOT as BASEDIR, BadCommandUsage
    37 from cubicweb.cwctl import CWCTL
    36 from cubicweb.cwctl import CWCTL
    38 from cubicweb.toolsutils import (SKEL_EXCLUDE, Command,
    37 from cubicweb.toolsutils import (SKEL_EXCLUDE, Command, copy_skeleton,
    39                                  copy_skeleton, underline_title)
    38                                  underline_title)
    40 from cubicweb.web.webconfig import WebConfiguration
    39 from cubicweb.web.webconfig import WebConfiguration
    41 from cubicweb.server.serverconfig import ServerConfiguration
    40 from cubicweb.server.serverconfig import ServerConfiguration
    42 
    41 
    43 
    42 
    44 class DevConfiguration(ServerConfiguration, WebConfiguration):
    43 class DevConfiguration(ServerConfiguration, WebConfiguration):
   100     notice that relation definitions description and static vocabulary
    99     notice that relation definitions description and static vocabulary
   101     should be marked using '_' and extracted using xgettext
   100     should be marked using '_' and extracted using xgettext
   102     """
   101     """
   103     from cubicweb.cwvreg import CubicWebVRegistry
   102     from cubicweb.cwvreg import CubicWebVRegistry
   104     if cubedir:
   103     if cubedir:
   105         cube = split(cubedir)[-1]
   104         cube = osp.split(cubedir)[-1]
   106         config = DevConfiguration(cube)
   105         config = DevConfiguration(cube)
   107         depcubes = list(config._cubes)
   106         depcubes = list(config._cubes)
   108         depcubes.remove(cube)
   107         depcubes.remove(cube)
   109         libconfig = DevConfiguration(*depcubes)
   108         libconfig = DevConfiguration(*depcubes)
   110     else:
   109     else:
   123     from copy import deepcopy
   122     from copy import deepcopy
   124     from cubicweb.i18n import add_msg
   123     from cubicweb.i18n import add_msg
   125     from cubicweb.web import uicfg
   124     from cubicweb.web import uicfg
   126     from cubicweb.schema import META_RTYPES, SYSTEM_RTYPES, CONSTRAINTS
   125     from cubicweb.schema import META_RTYPES, SYSTEM_RTYPES, CONSTRAINTS
   127     no_context_rtypes = META_RTYPES | SYSTEM_RTYPES
   126     no_context_rtypes = META_RTYPES | SYSTEM_RTYPES
   128     w('# schema pot file, generated on %s\n' % datetime.now().strftime('%Y-%m-%d %H:%M:%S'))
   127     w('# schema pot file, generated on %s\n'
       
   128       % datetime.now().strftime('%Y-%m-%d %H:%M:%S'))
   129     w('# \n')
   129     w('# \n')
   130     w('# singular and plural forms for each entity type\n')
   130     w('# singular and plural forms for each entity type\n')
   131     w('\n')
   131     w('\n')
   132     vregdone = set()
   132     vregdone = set()
   133     if libconfig is not None:
   133     if libconfig is not None:
   175                     add_msg(w, 'add a %s' % tschema,
   175                     add_msg(w, 'add a %s' % tschema,
   176                             'inlined:%s.%s.%s' % (etype, rschema, role))
   176                             'inlined:%s.%s.%s' % (etype, rschema, role))
   177                     add_msg(w, str(tschema),
   177                     add_msg(w, str(tschema),
   178                             'inlined:%s.%s.%s' % (etype, rschema, role))
   178                             'inlined:%s.%s.%s' % (etype, rschema, role))
   179                 if appearsin_addmenu.etype_get(eschema, rschema, role, tschema):
   179                 if appearsin_addmenu.etype_get(eschema, rschema, role, tschema):
   180                     if libconfig is not None and libappearsin_addmenu.etype_get(eschema, rschema, role, tschema):
   180                     if libconfig is not None and libappearsin_addmenu.etype_get(
       
   181                         eschema, rschema, role, tschema):
   181                         if eschema in libschema and tschema in libschema:
   182                         if eschema in libschema and tschema in libschema:
   182                             continue
   183                             continue
   183                     if role == 'subject':
   184                     if role == 'subject':
   184                         label = 'add %s %s %s %s' % (eschema, rschema,
   185                         label = 'add %s %s %s %s' % (eschema, rschema,
   185                                                      tschema, role)
   186                                                      tschema, role)
   198     w('# (no object form for final or symmetric relation types)\n')
   199     w('# (no object form for final or symmetric relation types)\n')
   199     w('\n')
   200     w('\n')
   200     for rschema in sorted(schema.relations()):
   201     for rschema in sorted(schema.relations()):
   201         rtype = rschema.type
   202         rtype = rschema.type
   202         if rtype not in libschema:
   203         if rtype not in libschema:
   203             # bw compat, necessary until all translation of relation are done properly...
   204             # bw compat, necessary until all translation of relation are done
       
   205             # properly...
   204             add_msg(w, rtype)
   206             add_msg(w, rtype)
   205             if rschema.description and rschema.description not in done:
   207             if rschema.description and rschema.description not in done:
   206                 done.add(rschema.description)
   208                 done.add(rschema.description)
   207                 add_msg(w, rschema.description)
   209                 add_msg(w, rschema.description)
   208             done.add(rtype)
   210             done.add(rtype)
   220                 libobjects = librschema and librschema.objects() or ()
   222                 libobjects = librschema and librschema.objects() or ()
   221                 for objschema in rschema.objects():
   223                 for objschema in rschema.objects():
   222                     if not objschema in libobjects:
   224                     if not objschema in libobjects:
   223                         add_msg(w, '%s_object' % rtype, objschema.type)
   225                         add_msg(w, '%s_object' % rtype, objschema.type)
   224             if rtype not in libschema:
   226             if rtype not in libschema:
   225                 # bw compat, necessary until all translation of relation are done properly...
   227                 # bw compat, necessary until all translation of relation are
       
   228                 # done properly...
   226                 add_msg(w, '%s_object' % rtype)
   229                 add_msg(w, '%s_object' % rtype)
   227     for objid in _iter_vreg_objids(vreg, vregdone):
   230     for objid in _iter_vreg_objids(vreg, vregdone):
   228         add_msg(w, '%s_description' % objid)
   231         add_msg(w, '%s_description' % objid)
   229         add_msg(w, objid)
   232         add_msg(w, objid)
   230 
   233 
   244                     yield objid
   247                     yield objid
   245                     done.add(objid)
   248                     done.add(objid)
   246                     break
   249                     break
   247 
   250 
   248 
   251 
   249 LANGS = ('en', 'fr', 'es')
       
   250 I18NDIR = join(BASEDIR, 'i18n')
       
   251 DEFAULT_POT_HEAD = r'''msgid ""
   252 DEFAULT_POT_HEAD = r'''msgid ""
   252 msgstr ""
   253 msgstr ""
   253 "Project-Id-Version: cubicweb %s\n"
   254 "Project-Id-Version: cubicweb %s\n"
   254 "PO-Revision-Date: 2008-03-28 18:14+0100\n"
   255 "PO-Revision-Date: 2008-03-28 18:14+0100\n"
   255 "Last-Translator: Logilab Team <contact@logilab.fr>\n"
   256 "Last-Translator: Logilab Team <contact@logilab.fr>\n"
   260 "Generated-By: cubicweb-devtools\n"
   261 "Generated-By: cubicweb-devtools\n"
   261 "Plural-Forms: nplurals=2; plural=(n > 1);\n"
   262 "Plural-Forms: nplurals=2; plural=(n > 1);\n"
   262 
   263 
   263 ''' % cubicwebversion
   264 ''' % cubicwebversion
   264 
   265 
       
   266 def cw_languages():
       
   267     for fname in listdir(osp.join(WebConfiguration.i18n_lib_dir())):
       
   268         if fname.endswith('.po'):
       
   269             yield osp.splitext(fname)[0]
       
   270 
   265 
   271 
   266 class UpdateCubicWebCatalogCommand(Command):
   272 class UpdateCubicWebCatalogCommand(Command):
   267     """Update i18n catalogs for cubicweb library.
   273     """Update i18n catalogs for cubicweb library.
   268 
   274 
   269     It will regenerate cubicweb/i18n/xx.po files. You'll have then to edit those
   275     It will regenerate cubicweb/i18n/xx.po files. You'll have then to edit those
   270     files to add translations of newly added messages.
   276     files to add translations of newly added messages.
   271     """
   277     """
   272     name = 'i18ncubicweb'
   278     name = 'i18ncubicweb'
       
   279     min_args = max_args = 0
   273 
   280 
   274     def run(self, args):
   281     def run(self, args):
   275         """run the command with its specific arguments"""
   282         """run the command with its specific arguments"""
   276         if args:
       
   277             raise BadCommandUsage('Too many arguments')
       
   278         import shutil
   283         import shutil
   279         import tempfile
   284         import tempfile
   280         import yams
   285         import yams
   281         from logilab.common.fileutils import ensure_fs_mode
   286         from logilab.common.fileutils import ensure_fs_mode
   282         from logilab.common.shellutils import globfind, find, rm
   287         from logilab.common.shellutils import globfind, find, rm
   283         from logilab.common.modutils import get_module_files
   288         from logilab.common.modutils import get_module_files
   284         from cubicweb.i18n import extract_from_tal, execute
   289         from cubicweb.i18n import extract_from_tal, execute
   285         tempdir = tempfile.mkdtemp()
   290         tempdir = tempfile.mkdtemp()
   286         potfiles = [join(I18NDIR, 'static-messages.pot')]
   291         cwi18ndir = WebConfiguration.i18n_lib_dir()
   287         print '-> extract schema messages.'
   292         print '-> extract schema messages.'
   288         schemapot = join(tempdir, 'schema.pot')
   293         schemapot = osp.join(tempdir, 'schema.pot')
       
   294         potfiles = [schemapot]
   289         potfiles.append(schemapot)
   295         potfiles.append(schemapot)
   290         # explicit close necessary else the file may not be yet flushed when
   296         # explicit close necessary else the file may not be yet flushed when
   291         # we'll using it below
   297         # we'll using it below
   292         schemapotstream = file(schemapot, 'w')
   298         schemapotstream = file(schemapot, 'w')
   293         generate_schema_pot(schemapotstream.write, cubedir=None)
   299         generate_schema_pot(schemapotstream.write, cubedir=None)
   294         schemapotstream.close()
   300         schemapotstream.close()
   295         print '-> extract TAL messages.'
   301         print '-> extract TAL messages.'
   296         tali18nfile = join(tempdir, 'tali18n.py')
   302         tali18nfile = osp.join(tempdir, 'tali18n.py')
   297         extract_from_tal(find(join(BASEDIR, 'web'), ('.py', '.pt')), tali18nfile)
   303         extract_from_tal(find(osp.join(BASEDIR, 'web'), ('.py', '.pt')),
       
   304                          tali18nfile)
   298         print '-> generate .pot files.'
   305         print '-> generate .pot files.'
   299         for id, files, lang in [('pycubicweb', get_module_files(BASEDIR) + list(globfind(join(BASEDIR, 'misc', 'migration'), '*.py')), None),
   306         pyfiles = get_module_files(BASEDIR)
   300                                 ('schemadescr', globfind(join(BASEDIR, 'schemas'), '*.py'), None),
   307         pyfiles += globfind(osp.join(BASEDIR, 'misc', 'migration'), '*.py')
       
   308         schemafiles = globfind(osp.join(BASEDIR, 'schemas'), '*.py')
       
   309         jsfiles = globfind(osp.join(BASEDIR, 'web'), 'cub*.js')
       
   310         for id, files, lang in [('pycubicweb', pyfiles, None),
       
   311                                 ('schemadescr', schemafiles, None),
   301                                 ('yams', get_module_files(yams.__path__[0]), None),
   312                                 ('yams', get_module_files(yams.__path__[0]), None),
   302                                 ('tal', [tali18nfile], None),
   313                                 ('tal', [tali18nfile], None),
   303                                 ('js', globfind(join(BASEDIR, 'web'), 'cub*.js'), 'java'),
   314                                 ('js', jsfiles, 'java'),
   304                                 ]:
   315                                 ]:
   305             cmd = 'xgettext --no-location --omit-header -k_ -o %s %s'
   316             cmd = 'xgettext --no-location --omit-header -k_ -o %s %s'
   306             if lang is not None:
   317             if lang is not None:
   307                 cmd += ' -L %s' % lang
   318                 cmd += ' -L %s' % lang
   308             potfile = join(tempdir, '%s.pot' % id)
   319             potfile = osp.join(tempdir, '%s.pot' % id)
   309             execute(cmd % (potfile, ' '.join('"%s"' % f for f in files)))
   320             execute(cmd % (potfile, ' '.join('"%s"' % f for f in files)))
   310             if exists(potfile):
   321             if osp.exists(potfile):
   311                 potfiles.append(potfile)
   322                 potfiles.append(potfile)
   312             else:
   323             else:
   313                 print '-> WARNING: %s file was not generated' % potfile
   324                 print '-> WARNING: %s file was not generated' % potfile
   314         print '-> merging %i .pot files' % len(potfiles)
   325         print '-> merging %i .pot files' % len(potfiles)
   315         cubicwebpot = join(tempdir, 'cubicweb.pot')
   326         cubicwebpot = osp.join(tempdir, 'cubicweb.pot')
   316         execute('msgcat -o %s %s' % (cubicwebpot, ' '.join('"%s"' % f for f in potfiles)))
   327         execute('msgcat -o %s %s'
       
   328                 % (cubicwebpot, ' '.join('"%s"' % f for f in potfiles)))
   317         print '-> merging main pot file with existing translations.'
   329         print '-> merging main pot file with existing translations.'
   318         chdir(I18NDIR)
   330         chdir(cwi18ndir)
   319         toedit = []
   331         toedit = []
   320         for lang in LANGS:
   332         for lang in cw_languages():
   321             target = '%s.po' % lang
   333             target = '%s.po' % lang
   322             execute('msgmerge -N --sort-output -o "%snew" "%s" "%s"' % (target, target, cubicwebpot))
   334             execute('msgmerge -N --sort-output -o "%snew" "%s" "%s"'
       
   335                     % (target, target, cubicwebpot))
   323             ensure_fs_mode(target)
   336             ensure_fs_mode(target)
   324             shutil.move('%snew' % target, target)
   337             shutil.move('%snew' % target, target)
   325             toedit.append(abspath(target))
   338             toedit.append(osp.abspath(target))
   326         # cleanup
   339         # cleanup
   327         rm(tempdir)
   340         rm(tempdir)
   328         # instructions pour la suite
   341         # instructions pour la suite
   329         print '-> regenerated CubicWeb\'s .po catalogs.'
   342         print '-> regenerated CubicWeb\'s .po catalogs.'
   330         print '\nYou can now edit the following files:'
   343         print '\nYou can now edit the following files:'
   344         if args:
   357         if args:
   345             cubes = [DevConfiguration.cube_dir(cube) for cube in args]
   358             cubes = [DevConfiguration.cube_dir(cube) for cube in args]
   346         else:
   359         else:
   347             cubes = [DevConfiguration.cube_dir(cube)
   360             cubes = [DevConfiguration.cube_dir(cube)
   348                      for cube in DevConfiguration.available_cubes()]
   361                      for cube in DevConfiguration.available_cubes()]
   349             cubes = [cubepath for cubepath in cubes if exists(join(cubepath, 'i18n'))]
   362             cubes = [cubepath for cubepath in cubes
       
   363                      if osp.exists(osp.join(cubepath, 'i18n'))]
   350         update_cubes_catalogs(cubes)
   364         update_cubes_catalogs(cubes)
   351 
   365 
   352 
   366 
   353 def update_cubes_catalogs(cubes):
   367 def update_cubes_catalogs(cubes):
   354     for cubedir in cubes:
   368     for cubedir in cubes:
   355         if not isdir(cubedir):
   369         if not osp.isdir(cubedir):
   356             print '-> ignoring %s that is not a directory.' % cubedir
   370             print '-> ignoring %s that is not a directory.' % cubedir
   357             continue
   371             continue
   358         try:
   372         try:
   359             toedit = update_cube_catalogs(cubedir)
   373             toedit = update_cube_catalogs(cubedir)
   360         except Exception:
   374         except Exception:
   374     import shutil
   388     import shutil
   375     import tempfile
   389     import tempfile
   376     from logilab.common.fileutils import ensure_fs_mode
   390     from logilab.common.fileutils import ensure_fs_mode
   377     from logilab.common.shellutils import find, rm
   391     from logilab.common.shellutils import find, rm
   378     from cubicweb.i18n import extract_from_tal, execute
   392     from cubicweb.i18n import extract_from_tal, execute
   379     cube = basename(normpath(cubedir))
   393     cube = osp.basename(osp.normpath(cubedir))
   380     tempdir = tempfile.mkdtemp()
   394     tempdir = tempfile.mkdtemp()
   381     print underline_title('Updating i18n catalogs for cube %s' % cube)
   395     print underline_title('Updating i18n catalogs for cube %s' % cube)
   382     chdir(cubedir)
   396     chdir(cubedir)
   383     if exists(join('i18n', 'entities.pot')):
   397     if osp.exists(osp.join('i18n', 'entities.pot')):
   384         warn('entities.pot is deprecated, rename file to static-messages.pot (%s)'
   398         warn('entities.pot is deprecated, rename file to static-messages.pot (%s)'
   385              % join('i18n', 'entities.pot'), DeprecationWarning)
   399              % osp.join('i18n', 'entities.pot'), DeprecationWarning)
   386         potfiles = [join('i18n', 'entities.pot')]
   400         potfiles = [osp.join('i18n', 'entities.pot')]
   387     elif exists(join('i18n', 'static-messages.pot')):
   401     elif osp.exists(osp.join('i18n', 'static-messages.pot')):
   388         potfiles = [join('i18n', 'static-messages.pot')]
   402         potfiles = [osp.join('i18n', 'static-messages.pot')]
   389     else:
   403     else:
   390         potfiles = []
   404         potfiles = []
   391     print '-> extract schema messages'
   405     print '-> extract schema messages'
   392     schemapot = join(tempdir, 'schema.pot')
   406     schemapot = osp.join(tempdir, 'schema.pot')
   393     potfiles.append(schemapot)
   407     potfiles.append(schemapot)
   394     # explicit close necessary else the file may not be yet flushed when
   408     # explicit close necessary else the file may not be yet flushed when
   395     # we'll using it below
   409     # we'll using it below
   396     schemapotstream = file(schemapot, 'w')
   410     schemapotstream = file(schemapot, 'w')
   397     generate_schema_pot(schemapotstream.write, cubedir)
   411     generate_schema_pot(schemapotstream.write, cubedir)
   398     schemapotstream.close()
   412     schemapotstream.close()
   399     print '-> extract TAL messages'
   413     print '-> extract TAL messages'
   400     tali18nfile = join(tempdir, 'tali18n.py')
   414     tali18nfile = osp.join(tempdir, 'tali18n.py')
   401     extract_from_tal(find('.', ('.py', '.pt'), blacklist=STD_BLACKLIST+('test',)), tali18nfile)
   415     ptfiles = find('.', ('.py', '.pt'), blacklist=STD_BLACKLIST+('test',))
       
   416     extract_from_tal(ptfiles, tali18nfile)
   402     print '-> extract Javascript messages'
   417     print '-> extract Javascript messages'
   403     jsfiles =  [jsfile for jsfile in find('.', '.js') if basename(jsfile).startswith('cub')]
   418     jsfiles =  [jsfile for jsfile in find('.', '.js')
       
   419                 if osp.basename(jsfile).startswith('cub')]
   404     if jsfiles:
   420     if jsfiles:
   405         tmppotfile = join(tempdir, 'js.pot')
   421         tmppotfile = osp.join(tempdir, 'js.pot')
   406         execute('xgettext --no-location --omit-header -k_ -L java --from-code=utf-8 -o %s %s'
   422         execute('xgettext --no-location --omit-header -k_ -L java '
   407                 % (tmppotfile, ' '.join(jsfiles)))
   423                 '--from-code=utf-8 -o %s %s' % (tmppotfile, ' '.join(jsfiles)))
   408         # no pot file created if there are no string to translate
   424         # no pot file created if there are no string to translate
   409         if exists(tmppotfile):
   425         if osp.exists(tmppotfile):
   410             potfiles.append(tmppotfile)
   426             potfiles.append(tmppotfile)
   411     print '-> create cube-specific catalog'
   427     print '-> create cube-specific catalog'
   412     tmppotfile = join(tempdir, 'generated.pot')
   428     tmppotfile = osp.join(tempdir, 'generated.pot')
   413     cubefiles = find('.', '.py', blacklist=STD_BLACKLIST+('test',))
   429     cubefiles = find('.', '.py', blacklist=STD_BLACKLIST+('test',))
   414     cubefiles.append(tali18nfile)
   430     cubefiles.append(tali18nfile)
   415     execute('xgettext --no-location --omit-header -k_ -o %s %s'
   431     execute('xgettext --no-location --omit-header -k_ -o %s %s'
   416             % (tmppotfile, ' '.join('"%s"' % f for f in cubefiles)))
   432             % (tmppotfile, ' '.join('"%s"' % f for f in cubefiles)))
   417     if exists(tmppotfile): # doesn't exists of no translation string found
   433     if osp.exists(tmppotfile): # doesn't exists of no translation string found
   418         potfiles.append(tmppotfile)
   434         potfiles.append(tmppotfile)
   419     potfile = join(tempdir, 'cube.pot')
   435     potfile = osp.join(tempdir, 'cube.pot')
   420     print '-> merging %i .pot files:' % len(potfiles)
   436     print '-> merging %i .pot files:' % len(potfiles)
   421     execute('msgcat -o %s %s' % (potfile,
   437     execute('msgcat -o %s %s' % (potfile,
   422                                  ' '.join('"%s"' % f for f in potfiles)))
   438                                  ' '.join('"%s"' % f for f in potfiles)))
   423     if not exists(potfile):
   439     if not osp.exists(potfile):
   424         print 'no message catalog for cube', cube, 'nothing to translate'
   440         print 'no message catalog for cube', cube, 'nothing to translate'
   425         # cleanup
   441         # cleanup
   426         rm(tempdir)
   442         rm(tempdir)
   427         return ()
   443         return ()
   428     print '-> merging main pot file with existing translations:'
   444     print '-> merging main pot file with existing translations:'
   429     chdir('i18n')
   445     chdir('i18n')
   430     toedit = []
   446     toedit = []
   431     for lang in LANGS:
   447     for lang in cw_languages():
   432         print '-> language', lang
   448         print '-> language', lang
   433         cubepo = '%s.po' % lang
   449         cubepo = '%s.po' % lang
   434         if not exists(cubepo):
   450         if not osp.exists(cubepo):
   435             shutil.copy(potfile, cubepo)
   451             shutil.copy(potfile, cubepo)
   436         else:
   452         else:
   437             execute('msgmerge -N -s -o %snew %s %s' % (cubepo, cubepo, potfile))
   453             execute('msgmerge -N -s -o %snew %s %s' % (cubepo, cubepo, potfile))
   438             ensure_fs_mode(cubepo)
   454             ensure_fs_mode(cubepo)
   439             shutil.move('%snew' % cubepo, cubepo)
   455             shutil.move('%snew' % cubepo, cubepo)
   440         toedit.append(abspath(cubepo))
   456         toedit.append(osp.abspath(cubepo))
   441     # cleanup
   457     # cleanup
   442     rm(tempdir)
   458     rm(tempdir)
   443     return toedit
   459     return toedit
   444 
   460 
   445 
   461 
   463     <cubename>
   479     <cubename>
   464       the name of the new cube. It should be a valid python module name.
   480       the name of the new cube. It should be a valid python module name.
   465     """
   481     """
   466     name = 'newcube'
   482     name = 'newcube'
   467     arguments = '<cubename>'
   483     arguments = '<cubename>'
   468 
   484     min_args = max_args = 1
   469     options = (
   485     options = (
   470         ("layout",
   486         ("layout",
   471          {'short': 'L', 'type' : 'choice', 'metavar': '<cube layout>',
   487          {'short': 'L', 'type' : 'choice', 'metavar': '<cube layout>',
   472           'default': 'simple', 'choices': ('simple', 'full'),
   488           'default': 'simple', 'choices': ('simple', 'full'),
   473           'help': 'cube layout. You\'ll get a minimal cube with the "simple" \
   489           'help': 'cube layout. You\'ll get a minimal cube with the "simple" \
   544         }
   560         }
   545 
   561 
   546     def run(self, args):
   562     def run(self, args):
   547         import re
   563         import re
   548         from logilab.common.shellutils import ASK
   564         from logilab.common.shellutils import ASK
   549         if len(args) != 1:
       
   550             raise BadCommandUsage("exactly one argument (cube name) is expected")
       
   551         cubename = args[0]
   565         cubename = args[0]
   552         if not re.match('[_A-Za-z][_A-Za-z0-9]*$', cubename):
   566         if not re.match('[_A-Za-z][_A-Za-z0-9]*$', cubename):
   553             raise BadCommandUsage("cube name should be a valid python module name")
   567             raise BadCommandUsage(
       
   568                 'cube name must be a valid python module name')
   554         verbose = self.get('verbose')
   569         verbose = self.get('verbose')
   555         cubesdir = self.get('directory')
   570         cubesdir = self.get('directory')
   556         if not cubesdir:
   571         if not cubesdir:
   557             cubespath = ServerConfiguration.cubes_search_path()
   572             cubespath = ServerConfiguration.cubes_search_path()
   558             if len(cubespath) > 1:
   573             if len(cubespath) > 1:
   559                 raise BadCommandUsage("can't guess directory where to put the new cube."
   574                 raise BadCommandUsage(
   560                                       " Please specify it using the --directory option")
   575                     "can't guess directory where to put the new cube."
       
   576                     " Please specify it using the --directory option")
   561             cubesdir = cubespath[0]
   577             cubesdir = cubespath[0]
   562         if not isdir(cubesdir):
   578         if not osp.isdir(cubesdir):
   563             print "-> creating cubes directory", cubesdir
   579             print "-> creating cubes directory", cubesdir
   564             try:
   580             try:
   565                 mkdir(cubesdir)
   581                 mkdir(cubesdir)
   566             except OSError, err:
   582             except OSError, err:
   567                 self.fail("failed to create directory %r\n(%s)" % (cubesdir, err))
   583                 self.fail("failed to create directory %r\n(%s)"
   568         cubedir = join(cubesdir, cubename)
   584                           % (cubesdir, err))
   569         if exists(cubedir):
   585         cubedir = osp.join(cubesdir, cubename)
   570             self.fail("%s already exists !" % (cubedir))
   586         if osp.exists(cubedir):
   571         skeldir = join(BASEDIR, 'skeleton')
   587             self.fail("%s already exists !" % cubedir)
       
   588         skeldir = osp.join(BASEDIR, 'skeleton')
   572         default_name = 'cubicweb-%s' % cubename.lower().replace('_', '-')
   589         default_name = 'cubicweb-%s' % cubename.lower().replace('_', '-')
   573         if verbose:
   590         if verbose:
   574             distname = raw_input('Debian name for your cube ? [%s]): ' % default_name).strip()
   591             distname = raw_input('Debian name for your cube ? [%s]): '
       
   592                                  % default_name).strip()
   575             if not distname:
   593             if not distname:
   576                 distname = default_name
   594                 distname = default_name
   577             elif not distname.startswith('cubicweb-'):
   595             elif not distname.startswith('cubicweb-'):
   578                 if ASK.confirm('Do you mean cubicweb-%s ?' % distname):
   596                 if ASK.confirm('Do you mean cubicweb-%s ?' % distname):
   579                     distname = 'cubicweb-' + distname
   597                     distname = 'cubicweb-' + distname
   580         else:
   598         else:
   581             distname = default_name
   599             distname = default_name
   582         if not re.match('[a-z][-a-z0-9]*$', distname):
   600         if not re.match('[a-z][-a-z0-9]*$', distname):
   583             raise BadCommandUsage("cube distname should be a valid debian package name")
   601             raise BadCommandUsage(
   584         longdesc = shortdesc = raw_input('Enter a short description for your cube: ')
   602                 'cube distname should be a valid debian package name')
       
   603         longdesc = shortdesc = raw_input(
       
   604             'Enter a short description for your cube: ')
   585         if verbose:
   605         if verbose:
   586             longdesc = raw_input('Enter a long description (leave empty to reuse the short one): ')
   606             longdesc = raw_input(
       
   607                 'Enter a long description (leave empty to reuse the short one): ')
   587         dependencies = {'cubicweb': '>= %s' % cubicwebversion}
   608         dependencies = {'cubicweb': '>= %s' % cubicwebversion}
   588         if verbose:
   609         if verbose:
   589             dependencies.update(self._ask_for_dependencies())
   610             dependencies.update(self._ask_for_dependencies())
   590         context = {'cubename' : cubename,
   611         context = {'cubename' : cubename,
   591                    'distname' : distname,
   612                    'distname' : distname,
   636     chances are the lines at the top are the ones that will bring the higher
   657     chances are the lines at the top are the ones that will bring the higher
   637     benefit after optimisation. Start there.
   658     benefit after optimisation. Start there.
   638     """
   659     """
   639     arguments = 'rql.log'
   660     arguments = 'rql.log'
   640     name = 'exlog'
   661     name = 'exlog'
   641     options = (
   662     options = ()
   642         )
       
   643 
   663 
   644     def run(self, args):
   664     def run(self, args):
   645         import re
   665         import re
   646         requests = {}
   666         requests = {}
   647         for filepath in args:
   667         for filepath in args:
   683 class GenerateSchema(Command):
   703 class GenerateSchema(Command):
   684     """Generate schema image for the given cube"""
   704     """Generate schema image for the given cube"""
   685     name = "schema"
   705     name = "schema"
   686     arguments = '<cube>'
   706     arguments = '<cube>'
   687     min_args = max_args = 1
   707     min_args = max_args = 1
   688     options = [('output-file', {'type':'file', 'default': None,
   708     options = [
   689                  'metavar': '<file>', 'short':'o', 'help':'output image file',
   709         ('output-file',
   690                  'input':False}),
   710          {'type':'file', 'default': None,
   691                ('viewer', {'type': 'string', 'default':None,
   711           'metavar': '<file>', 'short':'o', 'help':'output image file',
   692                 'short': "d", 'metavar':'<cmd>',
   712           'input':False,
   693                  'help':'command use to view the generated file (empty for none)'}
   713           }),
   694                ),
   714         ('viewer',
   695                ('show-meta', {'action': 'store_true', 'default':False,
   715          {'type': 'string', 'default':None,
   696                 'short': "m", 'metavar': "<yN>",
   716           'short': "d", 'metavar':'<cmd>',
   697                  'help':'include meta and internal entities in schema'}
   717           'help':'command use to view the generated file (empty for none)',
   698                ),
   718           }),
   699                ('show-workflow', {'action': 'store_true', 'default':False,
   719         ('show-meta',
   700                 'short': "w", 'metavar': "<yN>",
   720          {'action': 'store_true', 'default':False,
   701                 'help':'include workflow entities in schema'}
   721           'short': "m", 'metavar': "<yN>",
   702                ),
   722           'help':'include meta and internal entities in schema',
   703                ('show-cw-user', {'action': 'store_true', 'default':False,
   723           }),
   704                 'metavar': "<yN>",
   724         ('show-workflow',
   705                 'help':'include cubicweb user entities in schema'}
   725          {'action': 'store_true', 'default':False,
   706                ),
   726           'short': "w", 'metavar': "<yN>",
   707                ('exclude-type', {'type':'string', 'default':'',
   727           'help':'include workflow entities in schema',
   708                 'short': "x", 'metavar': "<types>",
   728           }),
   709                  'help':'coma separated list of entity types to remove from view'}
   729         ('show-cw-user',
   710                ),
   730          {'action': 'store_true', 'default':False,
   711                ('include-type', {'type':'string', 'default':'',
   731           'metavar': "<yN>",
   712                 'short': "i", 'metavar': "<types>",
   732           'help':'include cubicweb user entities in schema',
   713                  'help':'coma separated list of entity types to include in view'}
   733           }),
   714                ),
   734         ('exclude-type',
   715               ]
   735          {'type':'string', 'default':'',
       
   736           'short': "x", 'metavar': "<types>",
       
   737           'help':'coma separated list of entity types to remove from view',
       
   738           }),
       
   739         ('include-type',
       
   740          {'type':'string', 'default':'',
       
   741           'short': "i", 'metavar': "<types>",
       
   742           'help':'coma separated list of entity types to include in view',
       
   743           }),
       
   744         ]
   716 
   745 
   717     def run(self, args):
   746     def run(self, args):
   718         from subprocess import Popen
   747         from subprocess import Popen
   719         from tempfile import NamedTemporaryFile
   748         from tempfile import NamedTemporaryFile
   720         from logilab.common.textutils import splitstrip
   749         from logilab.common.textutils import splitstrip