Exemplo n.º 1
0
def setupDB():
    config = Config()
    dbConn = Database(config)

    # read SQL skeleton
    with open(os.path.join(os.getcwd(), 'setup/db_create.sql'), 'r') as f:
        sql = f.read()
    
    # fill in placeholders
    sql = sql.replace('&user', config.getProperty('Database', 'user'))

    # run SQL
    dbConn.execute(sql, None, None)

    # add admin user
    sql = '''
        INSERT INTO aide_admin.user (name, email, hash, issuperuser)
        VALUES (%s, %s, %s, %s)
        ON CONFLICT (name) DO NOTHING;
    '''
    adminPass = config.getProperty('Project', 'adminPassword')
    uHandler = UserHandling.backend.middleware.UserMiddleware(config)
    adminPass = uHandler._create_hash(adminPass.encode('utf8'))

    values = (config.getProperty('Project', 'adminName'), config.getProperty('Project', 'adminEmail'), adminPass, True,)
    dbConn.execute(sql, values, None)
Exemplo n.º 2
0
def migrate_aide():
    from modules import Database, UserHandling
    from util.configDef import Config

    config = Config()
    dbConn = Database(config)
    if dbConn.connectionPool is None:
        raise Exception('Error connecting to database.')

    warnings = []
    errors = []

    # bring all projects up-to-date (if registered within AIDE)
    projects = dbConn.execute('SELECT shortname FROM aide_admin.project;',
                              None, 'all')
    if projects is not None and len(projects):

        # get all schemata and check if project still exists
        schemata = dbConn.execute(
            'SELECT schema_name FROM information_schema.schemata', None, 'all')
        if schemata is not None and len(schemata):
            schemata = set([s['schema_name'].lower() for s in schemata])
            for p in projects:
                try:
                    pName = p['shortname']

                    # check if project still exists
                    if not pName.lower() in schemata:
                        warnings.append(
                            f'WARNING: project "{pName}" is registered but does not exist in database.'
                        )
                        #TODO: option to auto-remove?
                        continue

                    # make modifications one at a time
                    for mod in MODIFICATIONS_sql:
                        dbConn.execute(mod.format(schema=pName), None, None)
                except Exception as e:
                    errors.append(str(e))
        else:
            warnings.append(
                'WARNING: no project schemata found within database.')
    else:
        warnings.append('WARNING: no project registered within AIDE.')

    return warnings, errors
Exemplo n.º 3
0
    from util.configDef import Config
    from modules import Database

    if args.label_folder == '':
        args.label_folder = None

    if args.label_folder is not None and not args.label_folder.endswith(
            os.sep):
        args.label_folder += os.sep

    currentDT = datetime.datetime.now()
    currentDT = '{}-{}-{} {}:{}:{}'.format(currentDT.year, currentDT.month,
                                           currentDT.day, currentDT.hour,
                                           currentDT.minute, currentDT.second)

    config = Config()
    dbConn = Database(config)
    if not dbConn.canConnect():
        raise Exception('Error connecting to database.')

    # check if running on file server
    imgBaseDir = config.getProperty('FileServer', 'staticfiles_dir')
    if not os.path.isdir(imgBaseDir):
        raise Exception(
            '"{}" is not a valid directory on this machine. Are you running the script from the file server?'
            .format(imgBaseDir))

    if args.label_folder is not None and not os.path.isdir(args.label_folder):
        raise Exception(
            '"{}" is not a valid directory on this machine.'.format(
                args.label_folder))
Exemplo n.º 4
0
        description=
        'Upgrade and register an AIDE v1 project to an existing v2 installation.'
    )
    parser.add_argument(
        '--settings_filepath',
        type=str,
        help=
        'Path of the configuration .ini file for the v1 project to be upgraded to v2.'
    )
    args = parser.parse_args()

    from util.configDef import Config
    from modules import Database

    # v2 config file
    config = Config()
    dbConn = Database(config)
    if dbConn.connectionPool is None:
        raise Exception('Error connecting to database.')

    # v1 config file
    v1Config = Config(args.settings_filepath)

    # db schema of v1 project
    dbSchema = v1Config.getProperty('Database', 'schema')
    projectName = v1Config.getProperty('Project', 'projectName')

    # verify we're running a database on v2 standards
    isV2 = dbConn.execute(
        '''
        SELECT 1
Exemplo n.º 5
0
def main():

    # parse arguments
    parser = argparse.ArgumentParser(description='AIDE local model tester')
    parser.add_argument('--project',
                        type=str,
                        required=True,
                        help='Project shortname to draw sample data from.')
    parser.add_argument(
        '--mode',
        type=str,
        required=True,
        help=
        'Evaluation mode (function to call). One of {"train", "inference"}.')
    parser.add_argument(
        '--modelLibrary',
        type=str,
        required=False,
        help=
        'Optional AI model library override. Provide a dot-separated Python import path here.'
    )
    parser.add_argument(
        '--modelSettings',
        type=str,
        required=False,
        help=
        'Optional AI model settings override (absolute or relative path to settings file, or else "none" to not use any predefined settings).'
    )
    args = parser.parse_args()
    #TODO: continue

    assert args.mode.lower() in (
        'train', 'inference'), f'"{args.mode}" is not a known evaluation mode.'
    mode = args.mode.lower()

    # initialize required modules
    config = Config()
    dbConnector = Database(config)
    fileServer = FileServer(config).get_secure_instance(args.project)
    aiw = AIWorker(config, dbConnector, True)
    aicw = AIControllerWorker(config, None)

    # check if AIDE file server is reachable
    admin = AdminMiddleware(config, dbConnector)
    connDetails = admin.getServiceDetails(True, False)
    fsVersion = connDetails['FileServer']['aide_version']
    if not isinstance(fsVersion, str):
        # no file server running
        raise Exception(
            'ERROR: AIDE file server is not running, but required for running models. Make sure to launch it prior to running this script.'
        )
    elif fsVersion != AIDE_VERSION:
        print(
            f'WARNING: the AIDE version of File Server instance ({fsVersion}) differs from this one ({AIDE_VERSION}).'
        )

    # get model trainer instance and settings
    queryStr = '''
        SELECT ai_model_library, ai_model_settings FROM aide_admin.project
        WHERE shortname = %s;
    '''
    result = dbConnector.execute(queryStr, (args.project, ), 1)
    if result is None or not len(result):
        raise Exception(
            f'Project "{args.project}" could not be found in this installation of AIDE.'
        )

    modelLibrary = result[0]['ai_model_library']
    modelSettings = result[0]['ai_model_settings']

    customSettingsSpecified = False
    if hasattr(args, 'modelSettings') and isinstance(
            args.modelSettings, str) and len(args.modelSettings):
        # settings override specified
        if args.modelSettings.lower() == 'none':
            modelSettings = None
            customSettingsSpecified = True
        elif not os.path.isfile(args.modelSettings):
            print(
                f'WARNING: model settings override provided, but file cannot be found ("{args.modelSettings}"). Falling back to project default ("{modelSettings}").'
            )
        else:
            modelSettings = args.modelSettings
            customSettingsSpecified = True

    if hasattr(args, 'modelLibrary') and isinstance(
            args.modelLibrary, str) and len(args.modelLibrary):
        # library override specified; try to import it
        try:
            modelClass = helpers.get_class_executable(args.modelLibrary)
            if modelClass is None:
                raise
            modelLibrary = args.modelLibrary

            # re-check if current model settings are compatible; warn and set to None if not
            if modelLibrary != result[0][
                    'ai_model_library'] and not customSettingsSpecified:
                # project model settings are not compatible with provided model
                print(
                    'WARNING: custom model library specified differs from the one currently set in project. Model settings will be set to None.'
                )
                modelSettings = None

        except Exception as e:
            print(
                f'WARNING: model library override provided ("{args.modelLibrary}"), but could not be imported. Falling back to project default ("{modelLibrary}").'
            )

    # initialize instance
    print(f'Using model library "{modelLibrary}".')
    modelTrainer = aiw._init_model_instance(args.project, modelLibrary,
                                            modelSettings)

    stateDict = None  #TODO: load latest unless override is specified?

    # get data
    data = aicw.get_training_images(project=args.project, maxNumImages=512)
    data = __load_metadata(args.project, dbConnector, data[0],
                           (mode == 'train'))

    # helper functions
    def updateStateFun(state, message, done=None, total=None):
        print(message, end='')
        if done is not None and total is not None:
            print(f': {done}/{total}')
        else:
            print('')

    # launch task
    if mode == 'train':
        result = modelTrainer.train(stateDict, data, updateStateFun)
        if result is None:
            raise Exception(
                'Training function must return an object (i.e., trained model state) to be stored in the database.'
            )

    elif mode == 'inference':
        result = modelTrainer.inference(stateDict, data, updateStateFun)
Exemplo n.º 6
0
'''
    Decorator that enables Cross-Origin Resource Sharing (CORS).

    2019-20 Benjamin Kellenberger
'''

from urllib.parse import urlparse
import bottle
from bottle import request, response
from util.configDef import Config

# CORS is only required if the FileServer URI is a valid URL
fileServerURI = Config().getProperty('Server', 'dataServer_uri')
result = urlparse(fileServerURI)
cors_required = all([result.scheme,
                     result.netloc])  #TODO: might be too strict...


def enable_cors(fn):
    def _enable_cors(*args, **kwargs):
        # set CORS headers
        if cors_required:
            response.set_header('Access-Control-Allow-Origin', fileServerURI)
            response.set_header('Access-Control-Allow-Credentials', 'true')
            response.add_header('Access-Control-Allow-Methods',
                                'GET, POST, PUT, OPTIONS')
            response.add_header(
                'Access-Control-Allow-Headers',
                'Origin, Accept, Content-Type, X-Requested-With, X-CSRF-Token, *'
            )
    Function "init_celery_dispatcher" is to be initia-
    lized at launch time with a Celery app instance.

    2020 Benjamin Kellenberger
'''

import os
from celery import current_app
from .dataWorker import DataWorker
from util.configDef import Config

# initialise dataWorker
modules = os.environ['AIDE_MODULES']
passiveMode = (os.environ['PASSIVE_MODE'] == '1' if 'PASSIVE_MODE' in
               os.environ else False) or not ('fileserver' in modules.lower())
worker = DataWorker(Config())


@current_app.task()
def aide_internal_notify(message):
    return worker.aide_internal_notify(message)


@current_app.task(name='DataAdministration.list_images')
def listImages(project,
               imageAddedRange=None,
               lastViewedRange=None,
               viewcountRange=None,
               numAnnoRange=None,
               numPredRange=None,
               orderBy=None,
Exemplo n.º 8
0
def assemble_server(verbose_start=True,
                    check_v1_config=True,
                    migrate_database=True,
                    force_migrate=False,
                    passive_mode=False):

    # force verbosity if any of the pre-flight checks is enabled
    verbose_start = any((verbose_start, check_v1_config, migrate_database))

    instance_args = os.environ['AIDE_MODULES'].split(',')

    if verbose_start:
        configPath = os.environ['AIDE_CONFIG_PATH']
        aideModules = ', '.join(instance_args)

        print(f'''\033[96m
#################################       
                                        version {AIDE_VERSION}
   ###    #### ########  ########       
  ## ##    ##  ##     ## ##             {platform.platform()}
 ##   ##   ##  ##     ## ##             
##     ##  ##  ##     ## ######         [config]
#########  ##  ##     ## ##             .> {configPath}
##     ##  ##  ##     ## ##             
##     ## #### ########  ########       [modules]
                                        .> {aideModules}
#################################\033[0m
''')

    statusOffset = LogDecorator.get_ljust_offset()

    # load configuration
    config = Config(None, verbose_start)
    bottle.BaseRequest.MEMFILE_MAX = 1024**3  #TODO: make hyperparameter in config?

    # connect to database
    dbConnector = Database(config, verbose_start)

    if check_v1_config:
        # check if config file points to unmigrated v1 project
        print('Checking database...'.ljust(statusOffset), end='')
        hasAdminTable = dbConnector.execute(
            '''
                SELECT EXISTS (
                    SELECT FROM information_schema.tables 
                    WHERE table_schema = 'aide_admin'
                    AND table_name = 'project'
                );
            ''', None, 1)
        if not hasAdminTable[0]['exists']:
            # not (yet) migrated, raise Exception with instructions to ensure compatibility
            LogDecorator.print_status('fail')
            print(f'''
        The current installation of AIDE:
            database host: {config.getProperty('Database', 'host')}
            database name: {config.getProperty('Database', 'name')}
            schema:        {config.getProperty('Database', 'schema', str, '(not specified)')}

        points to an installation of the legacy AIDE v1.
        If you wish to continue using AIDE v2, you have to upgrade the project accordingly.
        For instructions to do so, see here:
            https://github.com/microsoft/aerial_wildlife_detection/blob/multiProject/doc/upgrade_from_v1.md
            ''')
            sys.exit(1)

        else:
            LogDecorator.print_status('ok')

        # check if projects have been migrated
        print('Checking projects...'.ljust(statusOffset), end='')
        dbSchema = config.getProperty('Database', 'schema', str, None)
        if dbSchema is not None:
            isMigrated = dbConnector.execute(
                '''
                    SELECT COUNT(*) AS cnt
                    FROM aide_admin.project
                    WHERE shortname = %s;
                ''', (dbSchema, ), 1)
            if isMigrated is not None and len(
                    isMigrated) and isMigrated[0]['cnt'] == 0:
                LogDecorator.print_status('warn')
                print(f'''
        WARNING: the selected configuration .ini file
        ("{os.environ['AIDE_CONFIG_PATH']}")
        points to a project that has not yet been migrated to AIDE v2.
        Details:
            database host: {config.getProperty('Database', 'host')}
            database name: {config.getProperty('Database', 'name')}
            schema:        {dbSchema}

        If you wish to continue using AIDE v2 for this project, you have to upgrade it
        to v2 accordingly.
        For instructions to do so, see here:
            https://github.com/microsoft/aerial_wildlife_detection/blob/multiProject/doc/upgrade_from_v1.md
            ''')
            else:
                LogDecorator.print_status('ok')
        else:
            LogDecorator.print_status('ok')

    if migrate_database:
        # bring AIDE up-to-date
        print('Updating database...'.ljust(statusOffset), end='')
        warnings, errors = migrate_aide(force_migrate)
        if len(warnings) or len(errors):
            if len(errors):
                LogDecorator.print_status('fail')
            else:
                LogDecorator.print_status('warn')

            print(
                f'Warnings and/or errors occurred while updating AIDE to the latest version ({AIDE_VERSION}):'
            )
            print('\nWarnings:')
            for w in warnings:
                print(f'\t"{w}"')

            print('\nErrors:')
            for e in errors:
                print(f'\t"{e}"')

            if len(errors):
                sys.exit(2)

        else:
            LogDecorator.print_status('ok')

    # prepare bottle
    app = Bottle()

    # parse requested instances
    instances = {}

    # "singletons"
    dbConnector = REGISTERED_MODULES['Database'](config, verbose_start)
    userHandler = REGISTERED_MODULES['UserHandler'](config, app, dbConnector)
    taskCoordinator = REGISTERED_MODULES['TaskCoordinator'](config, app,
                                                            dbConnector,
                                                            verbose_start)
    taskCoordinator.addLoginCheckFun(userHandler.checkAuthenticated)

    for i in instance_args:

        moduleName = i.strip()
        if moduleName == 'UserHandler':
            continue

        moduleClass = REGISTERED_MODULES[moduleName]

        # verify
        _verify_unique(instances, moduleClass)

        # create instance
        if moduleName == 'AIController':
            instance = moduleClass(config, app, dbConnector, taskCoordinator,
                                   verbose_start, passive_mode)
        else:
            instance = moduleClass(config, app, dbConnector, verbose_start)
        instances[moduleName] = instance

        # add authentication functionality
        if hasattr(instance, 'addLoginCheckFun'):
            instance.addLoginCheckFun(userHandler.checkAuthenticated)

        # launch project meta modules
        if moduleName == 'LabelUI':
            aideAdmin = REGISTERED_MODULES['AIDEAdmin'](config, app,
                                                        dbConnector,
                                                        verbose_start)
            aideAdmin.addLoginCheckFun(userHandler.checkAuthenticated)
            reception = REGISTERED_MODULES['Reception'](config, app,
                                                        dbConnector)
            reception.addLoginCheckFun(userHandler.checkAuthenticated)
            configurator = REGISTERED_MODULES['ProjectConfigurator'](
                config, app, dbConnector)
            configurator.addLoginCheckFun(userHandler.checkAuthenticated)
            statistics = REGISTERED_MODULES['ProjectStatistics'](config, app,
                                                                 dbConnector)
            statistics.addLoginCheckFun(userHandler.checkAuthenticated)

        elif moduleName == 'FileServer':
            from modules.DataAdministration.backend import celery_interface as daa_int

        elif moduleName == 'AIController':
            from modules.AIController.backend import celery_interface as aic_int

            # launch model marketplace with AIController
            modelMarketplace = REGISTERED_MODULES['ModelMarketplace'](
                config, app, dbConnector, taskCoordinator)
            modelMarketplace.addLoginCheckFun(userHandler.checkAuthenticated)

        elif moduleName == 'AIWorker':
            from modules.AIWorker.backend import celery_interface as aiw_int

        # launch globally required modules
        dataAdmin = REGISTERED_MODULES['DataAdministrator'](config, app,
                                                            dbConnector,
                                                            taskCoordinator)
        dataAdmin.addLoginCheckFun(userHandler.checkAuthenticated)

        staticFiles = REGISTERED_MODULES['StaticFileServer'](config, app,
                                                             dbConnector)
        staticFiles.addLoginCheckFun(userHandler.checkAuthenticated)

    if verbose_start:
        print('\n')

    return app
Exemplo n.º 9
0
    parser.add_argument(
        '--force_migrate',
        type=int,
        default=0,
        help=
        'If set to 1, database upgrade will be enforced even if AIDE versions already match.'
    )
    parser.add_argument(
        '--verbose',
        type=int,
        default=1,
        help='Set to 1 to print launch information to console.')
    args = parser.parse_args()

    try:
        app = assemble_server(args.verbose, args.check_v1, args.migrate_db,
                              args.force_migrate, not bool(args.launch))
    except Exception as e:
        print(e)
        sys.exit(1)

    if bool(args.launch):
        if args.verbose:
            print('Launching server...')
        config = Config(False)
        host = config.getProperty('Server', 'host')
        port = config.getProperty('Server', 'port')
        app.run(host=host, port=port)

    else:
        sys.exit(0)
    2020-21 Benjamin Kellenberger
'''

import os
from celery import current_app

from modules.AIController.backend.functional import AIControllerWorker
from util.configDef import Config

# init AIController middleware
modules = os.environ['AIDE_MODULES']
passiveMode = (os.environ['PASSIVE_MODE'] == '1'
               if 'PASSIVE_MODE' in os.environ else
               False) or not ('aicontroller' in modules.lower())
aim = AIControllerWorker(Config(), current_app)


@current_app.task(name='AIController.get_training_images')
def get_training_images(blank,
                        project,
                        epoch,
                        numEpochs,
                        minTimestamp='lastState',
                        includeGoldenQuestions=True,
                        minNumAnnoPerImage=0,
                        maxNumImages=None,
                        numWorkers=1):
    return aim.get_training_images(project, epoch, numEpochs, minTimestamp,
                                   includeGoldenQuestions, minNumAnnoPerImage,
                                   maxNumImages, numWorkers)
Exemplo n.º 11
0
def migrate_aide(forceMigrate=False):
    from modules import Database, UserHandling
    from util.configDef import Config
    
    config = Config()
    dbConn = Database(config)
    if not dbConn.canConnect():
        raise Exception('Error connecting to database.')
    
    warnings = []
    errors = []

    # skip if not forced and if database has same version
    doMigrate = True
    
    # check if DB has version already implemented
    dbVersion = None
    hasVersion = dbConn.execute('''
        SELECT EXISTS (
            SELECT FROM information_schema.tables 
            WHERE  table_schema = 'aide_admin'
            AND    table_name   = 'version'
        ) AS hasVersion;
    ''', None, 1)
    if hasVersion[0]['hasversion']:
        # check DB version
        dbVersion = dbConn.execute('SELECT version FROM aide_admin.version;', None, 1)
        if dbVersion is not None and len(dbVersion):
            dbVersion = dbVersion[0]['version']
            needsUpdate = version.compare_versions(version.AIDE_VERSION, dbVersion)
            if needsUpdate is not None:
                if needsUpdate < 0:
                    # running an older version of AIDE with a newer DB version
                    warnings.append(f'WARNING: local AIDE version ({version.AIDE_VERSION}) is older than the one in the database ({dbVersion}); please update your installation.')
                elif needsUpdate == 0:
                    doMigrate = False
                else:
                    doMigrate = True

    if not doMigrate and not forceMigrate:
        return warnings, errors

    # bring all projects up-to-date (if registered within AIDE)
    projects = dbConn.execute('SELECT shortname FROM aide_admin.project;', None, 'all')
    if projects is not None and len(projects):

        # get all schemata and check if project still exists
        schemata = dbConn.execute('SELECT schema_name FROM information_schema.schemata', None, 'all')
        if schemata is not None and len(schemata):
            schemata = set([s['schema_name'].lower() for s in schemata])
            for p in projects:
                try:
                    pName = p['shortname']

                    # check if project still exists
                    if not pName.lower() in schemata:
                        warnings.append(f'WARNING: project "{pName}" is registered but does not exist in database.')
                        #TODO: option to auto-remove?
                        continue

                    # special modification for CNN-to-labelclass map: drop only dep. on version (remove ancient tests)
                    if version.compare_versions(version.AIDE_VERSION, dbVersion) in (-1, None):
                        dbConn.execute(sql.SQL('DROP TABLE IF EXISTS {};').format(
                            sql.Identifier(pName, 'cnn_labelclass')
                        ), None)

                    # make modifications one at a time
                    for mod in MODIFICATIONS_sql:
                        dbConn.execute(mod.format(schema=pName), None, None)

                    # pre-official 2.0: mark existing CNN states as "labelclass_autoupdate" (as this was the default behavior)
                    if version.compare_versions(dbVersion, '2.0.210514') == -1:
                        dbConn.execute(sql.SQL('''
                            UPDATE {}
                            SET labelclass_autoupdate = TRUE;
                        ''').format(sql.Identifier(pName, 'cnnstate')), None)

                except Exception as e:
                    errors.append(str(e))
        else:
            warnings.append('WARNING: no project schemata found within database.')
    else:
        warnings.append('WARNING: no project registered within AIDE.')

    # update DB version accordingly
    dbConn.execute('''
        DELETE FROM aide_admin.version;
        INSERT INTO aide_admin.version (version)
        VALUES (%s);
    ''', (version.AIDE_VERSION, ))

    return warnings, errors
Exemplo n.º 12
0
def _verify_unique(instances, moduleClass):
    '''
            Compares the newly requested module, address and port against
            already launched modules on this instance.
            Raises an Exception if another module from the same type has already been launched on this instance
        '''
    for key in instances.keys():
        instance = instances[key]
        if moduleClass.__class__.__name__ == instance.__class__.__name__:
            raise Exception(
                'Module {} already launched on this server.'.format(
                    moduleClass.__class__.__name__))


# load configuration
config = Config(verbose_start=True)

# connect to database
dbConnector = Database(config, verbose_start=True)

# check if config file points to unmigrated v1 project
print('Checking database...'.ljust(10), end='')
hasAdminTable = dbConnector.execute(
    '''
        SELECT EXISTS (
            SELECT FROM information_schema.tables 
            WHERE table_schema = 'aide_admin'
            AND table_name = 'project'
        );
    ''', None, 1)
if not hasAdminTable[0]['exists']: