def ready(self): """ Startup method for Tethys Apps django app. """ # Perform App Harvesting harvester = SingletonAppHarvester() harvester.harvest_apps()
def sync_tethys_app_db(): """ Sync installed apps with database. """ # Get the harvester harvester = SingletonAppHarvester() try: # Make pass to remove apps that were uninstalled db_apps = TethysApp.objects.all() installed_app_packages = [app.package for app in harvester.apps] for db_apps in db_apps: if db_apps.package not in installed_app_packages: db_apps.delete() # Make pass to add apps to db that are newly installed installed_apps = harvester.apps for installed_app in installed_apps: # Query to see if installed app is in the database db_apps = TethysApp.objects.\ filter(package__exact=installed_app.package).\ all() # If the app is not in the database, then add it if len(db_apps) == 0: app = TethysApp( name=installed_app.name, package=installed_app.package, description=installed_app.description, enable_feedback=installed_app.enable_feedback, feedback_emails=installed_app.feedback_emails, index=installed_app.index, icon=installed_app.icon, root_url=installed_app.root_url, color=installed_app.color, tags=installed_app.tags ) app.save() # If the app is in the database, update developer-first attributes elif len(db_apps) == 1: db_app = db_apps[0] db_app.index = installed_app.index db_app.icon = installed_app.icon db_app.root_url = installed_app.root_url db_app.color = installed_app.color db_app.save() # More than one instance of the app in db... (what to do here?) elif len(db_apps) >= 2: continue except Exception as e: log.error(e)
def generate_app_url_patterns(): """ Generate the url pattern lists for each app and namespace them accordingly. """ # Get controllers list from app harvester harvester = SingletonAppHarvester() apps = harvester.apps app_url_patterns = dict() for app in apps: if hasattr(app, 'url_maps'): url_maps = app.url_maps() elif hasattr(app, 'controllers'): url_maps = app.controllers() else: url_maps = None if url_maps: for url_map in url_maps: app_root = app.root_url app_namespace = app_root.replace('-', '_') if app_namespace not in app_url_patterns: app_url_patterns[app_namespace] = [] # Create django url object if isinstance(url_map.controller, basestring): controller_parts = url_map.controller.split('.') module_name = '.'.join(controller_parts[:-1]) function_name = controller_parts[-1] try: module = __import__(module_name, fromlist=[function_name]) except ImportError: error_msg = 'The following error occurred while trying to import the controller function ' \ '"{0}":\n {1}'.format(url_map.controller, traceback.format_exc(2)) log.error(error_msg) sys.exit(1) try: controller_function = getattr(module, function_name) except AttributeError, e: error_msg = 'The following error occurred while tyring to access the controller function ' \ '"{0}":\n {1}'.format(url_map.controller, traceback.format_exc(2)) log.error(error_msg) sys.exit(1) else: controller_function = url_map.controller django_url = url(url_map.url, controller_function, name=url_map.name) # Append to namespace list app_url_patterns[app_namespace].append(django_url)
def get_app_url_patterns(): """ Generate the url pattern lists for each app and namespace them accordingly. """ # Get controllers list from app harvester harvester = SingletonAppHarvester() apps = harvester.apps app_url_patterns = dict() for app in apps: app_url_patterns.update(app.url_patterns) return app_url_patterns
def generate_app_url_patterns(): """ Generate the url pattern lists for each app and namespace them accordingly. """ # Get controllers list from app harvester harvester = SingletonAppHarvester() apps = harvester.apps app_url_patterns = dict() for app in apps: if hasattr(app, 'url_maps'): url_maps = app.url_maps() elif hasattr(app, 'controllers'): url_maps = app.controllers() else: url_maps = None if url_maps: for url_map in url_maps: app_root = app.root_url app_namespace = app_root.replace('-', '_') if app_namespace not in app_url_patterns: app_url_patterns[app_namespace] = [] # Create django url object if isinstance(url_map.controller, basestring): controller_parts = url_map.controller.split('.') module_name = '.'.join(controller_parts[:-1]) function_name = controller_parts[-1] try: module = __import__(module_name, fromlist=[function_name]) except ImportError: raise ValueError('"{0}" is not a valid controller function.'.format(url_map.controller)) try: controller_function = getattr(module, function_name) except AttributeError: raise ValueError('"{0}" is not a valid controller function.'.format(url_map.controller)) else: controller_function = url_map.controller django_url = url(url_map.url, controller_function, name=url_map.name) # Append to namespace list app_url_patterns[app_namespace].append(django_url) return app_url_patterns
def tethys_apps_context(request): """ Add the current Tethys app metadata to the template context. """ # Setup variables harvester = SingletonAppHarvester() context = {'tethys_app': None} apps_root = 'apps' # Get url and parts url = request.path url_parts = url.split('/') # Find the app key if apps_root in url_parts: # The app root_url is the path item following (+1) the apps_root item app_root_url_index = url_parts.index(apps_root) + 1 app_root_url = url_parts[app_root_url_index] # Get list of app dictionaries from the harvester apps = harvester.apps # If a match can be made, return the app dictionary as part of the context for app in apps: if app.root_url == app_root_url: context['tethys_app'] = { 'name': app.name, 'index': app.index, 'icon': app.icon, 'color': app.color, 'description': app.description } if hasattr(app, 'feedback_emails') and len(app.feedback_emails) > 0: context['tethys_app'][ 'feedback_emails'] = app.feedback_emails if hasattr(app, 'enable_feedback'): context['tethys_app'][ 'enable_feedback'] = app.enable_feedback return context
""" ******************************************************************************** * Name: models.py * Author: Nathan Swain * Created On: 2014 * Copyright: (c) Brigham Young University 2014 * License: BSD 2-Clause ******************************************************************************** """ from tethys_apps.app_harvester import SingletonAppHarvester # Perform App Harvesting harvester = SingletonAppHarvester() harvester.harvest_apps()
def register_app_permissions(): """ Register and sync the app permissions. """ from guardian.shortcuts import assign_perm, remove_perm, get_perms from django.contrib.contenttypes.models import ContentType from django.contrib.auth.models import Permission, Group # Get the apps harvester = SingletonAppHarvester() apps = harvester.apps all_app_permissions = {} all_groups = {} for app in apps: perms = app.permissions() # Name spaced prefix for app permissions # e.g. my_first_app:view_things # e.g. my_first_app | View things perm_codename_prefix = app.package + ':' perm_name_prefix = app.package + ' | ' if perms is not None: # Thing is either a Permission or a PermissionGroup object for thing in perms: # Permission Case if isinstance(thing, permissions.Permission): # Name space the permissions and add it to the list permission_codename = perm_codename_prefix + thing.name permission_name = perm_name_prefix + thing.description all_app_permissions[permission_codename] = permission_name # PermissionGroup Case elif isinstance(thing, permissions.PermissionGroup): # Record in dict of groups group_permissions = [] group_name = perm_codename_prefix + thing.name for perm in thing.permissions: # Name space the permissions and add it to the list permission_codename = perm_codename_prefix + perm.name permission_name = perm_name_prefix + perm.description all_app_permissions[permission_codename] = permission_name group_permissions.append(permission_codename) # Store all groups for all apps all_groups[group_name] = {'permissions': group_permissions, 'app_package': app.package} # Get the TethysApp content type tethys_content_type = ContentType.objects.get( app_label='tethys_apps', model='tethysapp' ) # Remove any permissions that no longer exist db_app_permissions = Permission.objects.filter(content_type=tethys_content_type).all() for db_app_permission in db_app_permissions: # Delete the permission if the permission is no longer required by an app if db_app_permission.codename not in all_app_permissions: db_app_permission.delete() # Create permissions that need to be created for perm in all_app_permissions: # Create permission if it doesn't exist try: # If permission exists, update it p = Permission.objects.get(codename=perm) p.name = all_app_permissions[perm] p.content_type = tethys_content_type p.save() except Permission.DoesNotExist: p = Permission( name=all_app_permissions[perm], codename=perm, content_type=tethys_content_type ) p.save() # Remove any groups that no longer exist db_groups = Group.objects.all() db_apps = TethysApp.objects.all() db_app_names = [db_app.package for db_app in db_apps] for db_group in db_groups: db_group_name_parts = db_group.name.split(':') # Only perform maintenance on groups that belong to Tethys Apps if (len(db_group_name_parts) > 1) and (db_group_name_parts[0] in db_app_names): # Delete groups that is no longer required by an app if db_group.name not in all_groups: db_group.delete() # Create groups that need to be created for group in all_groups: # Look up the app db_app = TethysApp.objects.get(package=all_groups[group]['app_package']) # Create group if it doesn't exist try: # If it exists, update the permissions assigned to it g = Group.objects.get(name=group) # Get the permissions for the group and remove all of them perms = get_perms(g, db_app) for p in perms: remove_perm(p, g, db_app) # Assign the permission to the group and the app instance for p in all_groups[group]['permissions']: assign_perm(p, g, db_app) except Group.DoesNotExist: # Create a new group g = Group(name=group) g.save() # Assign the permission to the group and the app instance for p in all_groups[group]['permissions']: assign_perm(p, g, db_app)
def provision_persistent_stores(self, app_names, options): """ Provision all persistent stores for all apps or for only the app name given. """ # Set refresh parameter database_refresh = options['refresh'] # Get the app harvester app_harvester = SingletonAppHarvester() # Define the list of target apps target_apps = [] target_apps_check = [] # Execute on all apps loaded if ALL_APPS in app_names: target_apps = app_harvester.apps # Execute only on apps given else: for app in app_harvester.apps: # Derive app_name from the index which follows the pattern app_name:home if app.package in app_names: target_apps.append(app) target_apps_check.append(app.package) # Verify all apps included in target apps for app_name in app_names: if app_name not in target_apps_check: self.stdout.write('{0}WARNING:{1} The app named "{2}" cannot be found. Please make sure it is installed ' 'and try again.'.format(TerminalColors.WARNING, TerminalColors.ENDC, app_name)) # Notify user of database provisioning self.stdout.write(TerminalColors.BLUE + '\nProvisioning Persistent Stores...' + TerminalColors.ENDC) # Get database manager url from the config database_manager_db = settings.TETHYS_DATABASES['tethys_db_manager'] database_manager_url = 'postgresql://{0}:{1}@{2}:{3}/{4}'.format(database_manager_db['USER'] if 'USER' in database_manager_db else 'tethys_db_manager', database_manager_db['PASSWORD'] if 'PASSWORD' in database_manager_db else 'pass', database_manager_db['HOST'] if 'HOST' in database_manager_db else '127.0.0.1', database_manager_db['PORT'] if 'PORT' in database_manager_db else '5435', database_manager_db['NAME'] if 'NAME' in database_manager_db else 'tethys_db_manager') database_manager_name = database_manager_url.split('://')[1].split(':')[0] #--------------------------------------------------------------------------------------------------------------# # Get a list of existing databases #--------------------------------------------------------------------------------------------------------------# # Create connection engine engine = create_engine(database_manager_url) # Cannot create databases in a transaction: connect and commit to close transaction connection = engine.connect() # Check for Database existing_dbs_statement = ''' SELECT d.datname as name FROM pg_catalog.pg_database d LEFT JOIN pg_catalog.pg_user u ON d.datdba = u.usesysid ORDER BY 1; ''' existing_dbs = connection.execute(existing_dbs_statement) connection.close() # Compile list of db names existing_db_names = [] for existing_db in existing_dbs: existing_db_names.append(existing_db.name) # Get apps and provision persistent stores if not already created for app in target_apps: # Create multiple persistent stores if necessary persistent_stores = app.persistent_stores() if persistent_stores: # Assemble list of target persistent stores target_persistent_stores = [] # Target the persistent store provided if options['database']: for persistent_store in persistent_stores: if options['database'] == persistent_store.name: target_persistent_stores.append(persistent_store) # Target all persistent stores else: target_persistent_stores = persistent_stores for persistent_store in target_persistent_stores: full_db_name = '_'.join((app.package, persistent_store.name)) new_database = True #--------------------------------------------------------------------------------------------------# # 1. Drop database if refresh option is included #--------------------------------------------------------------------------------------------------# if database_refresh and full_db_name in existing_db_names: # Provide update for user self.stdout.write('Dropping database {2}"{0}"{3} for app {2}"{1}"{3}...'.format( persistent_store.name, app.package, TerminalColors.BLUE, TerminalColors.ENDC )) # Connection delete_connection = engine.connect() # Drop db drop_db_statement = 'DROP DATABASE IF EXISTS {0}'.format(full_db_name) # Close transaction first then execute. delete_connection.execute('commit') delete_connection.execute(drop_db_statement) delete_connection.close() # Update the existing dbs query existing_db_names.pop(existing_db_names.index(full_db_name)) #--------------------------------------------------------------------------------------------------# # 2. Create the database if it does not already exist #--------------------------------------------------------------------------------------------------# if full_db_name not in existing_db_names: # Provide Update for User self.stdout.write('Creating database {2}"{0}"{3} for app {2}"{1}"{3}...'.format( persistent_store.name, app.package, TerminalColors.BLUE, TerminalColors.ENDC )) # Cannot create databases in a transaction: connect and commit to close transaction create_connection = engine.connect() # Create db create_db_statement = ''' CREATE DATABASE {0} WITH OWNER {1} TEMPLATE template0 ENCODING 'UTF8' '''.format(full_db_name, database_manager_name) # Close transaction first and then execute create_connection.execute('commit') create_connection.execute(create_db_statement) create_connection.close() else: # Provide Update for User self.stdout.write('Database {2}"{0}"{3} already exists for app {2}"{1}"{3}, skipping...'.format( persistent_store.name, app.package, TerminalColors.BLUE, TerminalColors.ENDC )) # Set var that is passed to initialization functions new_database = False #--------------------------------------------------------------------------------------------------# # 3. Enable PostGIS extension #--------------------------------------------------------------------------------------------------# if (hasattr(persistent_store, 'spatial') and persistent_store.spatial) or persistent_store.postgis: # Get URL for Tethys Superuser to enable extensions super_db = settings.TETHYS_DATABASES['tethys_super'] super_url = 'postgresql://{0}:{1}@{2}:{3}/{4}'.format(super_db['USER'] if 'USER' in super_db else 'tethys_super', super_db['PASSWORD'] if 'PASSWORD' in super_db else 'pass', super_db['HOST'] if 'HOST' in super_db else '127.0.0.1', super_db['PORT'] if 'PORT' in super_db else '5435', super_db['NAME'] if 'NAME' in super_db else 'tethys_super') super_parts = super_url.split('/') new_db_url = '{0}//{1}/{2}'.format(super_parts[0], super_parts[2], full_db_name) # Connect to new database new_db_engine = create_engine(new_db_url) new_db_connection = new_db_engine.connect() # Notify user self.stdout.write('Enabling PostGIS on database {2}"{0}"{3} for app {2}"{1}"{3}...'.format( persistent_store.name, app.package, TerminalColors.BLUE, TerminalColors.ENDC )) enable_postgis_statement = 'CREATE EXTENSION IF NOT EXISTS postgis' # Execute postgis statement new_db_connection.execute(enable_postgis_statement) new_db_connection.close() #------------------------------------------------------------------------------------------------------# # 4. Run initialization functions for each store here #------------------------------------------------------------------------------------------------------# for persistent_store in target_persistent_stores: if persistent_store.initializer_is_valid: initializer = persistent_store.initializer_function else: if ':' in persistent_store.initializer: print('DEPRECATION WARNING: The initializer attribute of a PersistentStore should now be in the form: "my_first_app.init_stores.init_spatial_db". The form "init_stores:init_spatial_db" is now deprecated.') # Split into module name and function name initializer_mod, initializer_function = persistent_store.initializer.split(':') # Pre-process initializer path initializer_path = '.'.join(('tethys_apps.tethysapp', app.package, initializer_mod)) try: # Import module module = __import__(initializer_path, fromlist=[initializer_function]) except ImportError: pass else: # Get the function initializer = getattr(module, initializer_function) try: if not initializer: raise ValueError('"{0}" is not a valid function.'.format(persistent_store.initializer)) except UnboundLocalError: raise ValueError('"{0}" is not a valid function.'.format(persistent_store.initializer)) self.stdout.write('Initializing database {3}"{0}"{4} for app {3}"{1}"{4} using initializer ' '{3}"{2}"{4}...'.format(persistent_store.name, app.package, initializer.__name__, TerminalColors.BLUE, TerminalColors.ENDC )) if options['first_time']: initializer(True) else: initializer(new_database)