Source code for camelot.bin.meta

#  ============================================================================
#
#  Copyright (C) 2007-2016 Conceptive Engineering bvba.
#  www.conceptive.be / info@conceptive.be
#
#  Redistribution and use in source and binary forms, with or without
#  modification, are permitted provided that the following conditions are met:
#      * Redistributions of source code must retain the above copyright
#        notice, this list of conditions and the following disclaimer.
#      * Redistributions in binary form must reproduce the above copyright
#        notice, this list of conditions and the following disclaimer in the
#        documentation and/or other materials provided with the distribution.
#      * Neither the name of Conceptive Engineering nor the
#        names of its contributors may be used to endorse or promote products
#        derived from this software without specific prior written permission.
#  
#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
#  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
#  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
#  DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
#  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
#  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
#  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
#  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
#  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
#  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
#  ============================================================================
"""
Utility functions and classes to start a new Camelot project, this
could be the start of MetaCamelot
"""

import os
import logging

from ..core.qt import QtWidgets
from ..core.utils import ugettext_lazy as _
from ..admin.application_admin import ApplicationAdmin
from ..admin.object_admin import ObjectAdmin
from ..admin.action import Action
from ..view.controls import delegates
from ..view.main import main_action

LOGGER = logging.getLogger( 'camelot.bin.meta' )

[docs]class MetaCamelotAdmin( ApplicationAdmin ): """ApplicationAdmin class to be used within meta camelot""" name = 'Meta Camelot'
def launch_meta_camelot(): action = CreateNewProject() main_action(action) # # The various features that can be set when creating a new Camelot project # features = [ ('source', '.', delegates.LocalFileDelegate, '''The directory in which to create<br/>''' '''the sources of the new project '''), ('name', 'My Application', delegates.PlainTextDelegate, '''The name of the application<br/>''' '''as it will appear in the main window and<br/>''' '''will be used to store settings in the<br/>''' '''registry.'''), ('author', 'My Company', delegates.PlainTextDelegate, '''The author of the application, this<br/>''' '''will be used to store settings in the<br/>''' '''registry.'''), ('module', 'myapplication', delegates.PlainTextDelegate, '''The name of the python module that<br/>''' '''will contain the application'''), ('domain', 'mydomain.com', delegates.PlainTextDelegate, '''The domain name of the author, this will<br/>''' '''be used to store settings in the registry'''), ('application_url', 'http://www.python-camelot.com', delegates.PlainTextDelegate, '''Url of the application, this url should be<br/>''' '''unique for the application, as it will be used<br/>''' '''to uniquely identify the application in Windows'''), ('help_url', 'http://www.python-camelot.com/docs.html', delegates.PlainTextDelegate, '''Part of the website with online help'''), ('installer', False, delegates.BoolDelegate, '''Build a windows installer'''), ] # # Templates of the files to create when starting a new project # templates = [ ('{{options.module}}/application_admin.py', ''' from camelot.view.art import Icon from camelot.admin.application_admin import ApplicationAdmin from camelot.admin.section import Section from camelot.core.utils import ugettext_lazy as _ class MyApplicationAdmin(ApplicationAdmin): name = '{{options.name}}' application_url = '{{options.application_url}}' help_url = '{{options.help_url}}' author = '{{options.author}}' domain = '{{options.domain}}' def get_sections(self): from camelot.model.memento import Memento from camelot.model.i18n import Translation return [ Section( _('My classes'), self, Icon('tango/22x22/apps/system-users.png'), items = [] ), Section( _('Configuration'), self, Icon('tango/22x22/categories/preferences-system.png'), items = [Memento, Translation] ) ] '''), ('__init__.py', ''), ('{{options.module}}/__init__.py', ''), ('{{options.module}}/test.py', ''' # # Default unittests for a camelot application. These unittests will create # screenshots of all the views in the application. Run them with this command : # # python -m nose.core -v -s {{options.module}}/test.py # import os from camelot.test import EntityViewsTest # screenshots will be put in this directory static_images_path = os.path.join( os.path.dirname( __file__ ), 'images' ) class MyApplicationViewsTest( EntityViewsTest ): images_path = static_images_path '''), ('main.py', ''' import logging from camelot.core.conf import settings, SimpleSettings logging.basicConfig( level = logging.ERROR ) logger = logging.getLogger( 'main' ) # begin custom settings class MySettings( SimpleSettings ): # add an ENGINE or a CAMELOT_MEDIA_ROOT method here to connect # to another database or change the location where files are stored # # def ENGINE( self ): # from sqlalchemy import create_engine # return create_engine( 'postgresql://user:passwd@127.0.0.1/database' ) def setup_model( self ): """This function will be called at application startup, it is used to setup the model""" from camelot.core.sql import metadata from camelot.core.orm import setup_all metadata.bind = self.ENGINE() import camelot.model.authentication import camelot.model.i18n import camelot.model.memento import {{options.module}}.model setup_all() metadata.create_all() my_settings = MySettings( '{{options.author}}', '{{options.name}}' ) settings.append( my_settings ) # end custom settings def start_application(): from camelot.admin.action.application import Application from camelot.view.main import main_action from {{options.module}}.application_admin import MyApplicationAdmin application = Application(MyApplicationAdmin()) main_action(application) if __name__ == '__main__': start_application() '''), ('{{options.module}}/model.py', ''' from sqlalchemy.schema import Column import sqlalchemy.types from camelot.admin.entity_admin import EntityAdmin from camelot.core.orm import Entity import camelot.types '''), ('excludes.txt', r''' vtk* sphinx* Lib\site-packages\cvxopt* Lib\site-packages\IPython* Lib\site-packages\logilab* Lib\site-packages\nose* Lib\site-packages\PIL* Lib\site-packages\py2exe* Lib\site-packages\pyflakes* Lib\site-packages\pylint* Lib\site-packages\pytz\zoneinfo\* Lib\site-packages\rope* Lib\site-packages\Sphinx* Lib\site-packages\spyder* Lib\site-packages\virtualenv* Lib\site-packages\VTK* Lib\site-packages\docutils* Lib\site-packages\pyreadline* Lib\site-packages\Bio* Lib\site-packages\vitables* Lib\site-packages\sympy* Lib\site-packages\Cython* Lib\site-packages\sympy* Lib\site-packages\PyOpenGL* Lib\site-packages\tables* Lib\site-packages\zmq* include license libs '''), ('setup.py', ''' # # Default setup file for a Camelot application # # To build a windows installer, execute this file with : # # python setup.py egg_info bdist_cloud wininst_cloud # # Running from the Python SDK command line # import datetime import logging from setuptools import setup, find_packages logging.basicConfig( level=logging.INFO ) setup( name = '{{options.name}}', version = '1.0', author = '{{options.author}}', url = '{{options.application_url}}', include_package_data = True, packages = find_packages(), py_modules = ['settings', 'main'], entry_points = {'gui_scripts':[ 'main = main:start_application', ],}, options = { 'bdist_cloud':{'revision':'0', 'branch':'master', 'uuid':'{{uuid}}', 'update_before_launch':False, 'default_entry_point':('gui_scripts','main'), 'changes':[], 'timestamp':datetime.datetime.now(), }, 'wininst_cloud':{ 'excludes':'excludes.txt', 'uuid':'{{uuid}}', }, }, ) '''), ] class NewProjectOptions(object): def __init__(self): for feature in features: setattr( self, feature[0], feature[1] ) class Admin( ObjectAdmin ): verbose_name = _('New project') form_display = [feature[0] for feature in features] field_attributes = dict( ( feature[0],{ 'editable':True, 'delegate':feature[2], 'nullable':False, 'tooltip':feature[3] } ) for feature in features) field_attributes['source']['directory'] = True
[docs]class CreateNewProject( Action ): """Action to create a new project, based on a form with options the user fills in.""" def model_run(self, model_context): # begin change object from camelot.view import action_steps app_admin = MetaCamelotAdmin() options = NewProjectOptions() yield action_steps.UpdateProgress( text = 'Request information' ) yield action_steps.ChangeObject( options, app_admin.get_related_admin(NewProjectOptions) ) # end change object yield action_steps.UpdateProgress( text = 'Creating new project' ) self.start_project( options ) project_path = os.path.abspath( options.source ) if options.installer: cloudlaunch_found = False try: import cloudlaunch cloudlaunch_found = True except Exception: yield action_steps.MessageBox( 'To build a Windows installer, you need to be using<br/>' \ 'the Conceptive Python SDK, please visit<br/>' \ '<a href="http://www.conceptive.be/python-sdk.html">www.conceptive.be/python-sdk.html</a><br/>' \ 'for more information' ) if cloudlaunch_found: LOGGER.debug( '%s imported'%( cloudlaunch.__name__ ) ) yield action_steps.UpdateProgress( text = 'Building windows installer' ) import distutils.core current_dir = os.getcwd() # change to the app directory for setuptools to do its job os.chdir( project_path ) setup_path = os.path.join( 'setup.py' ) distribution = distutils.core.run_setup( setup_path, script_args = ['egg_info', 'bdist_cloud', 'wininst_cloud'] ) os.chdir( current_dir ) for command, _python_version, filename in distribution.dist_files: if command == 'wininst_cloud': yield action_steps.MessageBox( 'Use Inno Setup to process the file<br/>' \ '<b>%s</b><br/> to build the installer executable'% os.path.join( project_path, filename ), standard_buttons = QtWidgets.QMessageBox.Ok ) yield action_steps.MessageBox( 'All files for the new project<br/>' \ 'were created in <b>%s</b>'%project_path, standard_buttons = QtWidgets.QMessageBox.Ok ) yield action_steps.OpenFile( project_path ) def start_project( self, options ): from jinja2 import Template import uuid context = {'options':options, 'uuid':str(uuid.uuid4())} if not os.path.exists( os.path.join( options.source, options.module ) ): os.makedirs( os.path.join( options.source, options.module ) ) for filename_template, code_template in templates: filename = Template( filename_template ).render( context ) code = Template( code_template ).render( context ) fp = open( os.path.join( options.source, filename ), 'w' ) fp.write( code ) fp.close()