apel/db/apeldb.py

Summary

Maintainability
D
1 day
Test Coverage
'''
   Copyright (C) 2012 STFC

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

   @author: Konrad Jopek
'''

import logging
from apel.db import LOGGER_ID

logger = logging.getLogger(LOGGER_ID)

class ApelDbException(Exception):
    pass

class ApelDb(object):
    '''
    Interface for any database used by the record loader.  Implementations
    should implement the methods.
    '''
    def __new__(cls, backend, host, port, username, pwd, db):
        '''
        Constructs backend object.

        This class behaves as a factory. You cannot instantiate it.
        '''

        BACKENDS = {}

        try:
            from apel.db.backends.mysql import ApelMysqlDb
            BACKENDS['mysql'] = ApelMysqlDb
        except ImportError:
            logger.info('Cannot import mysql backend')

        try:
            from apel.db.backends.oracle import ApelOracleDb
            BACKENDS['oracle'] = ApelOracleDb
        except ImportError:
            logger.debug('Cannot import oracle backend')

        if backend not in BACKENDS.keys():
            raise ApelDbException('Unknown backend: %s' % (backend))

        backend = BACKENDS[backend]
        return backend(host, port, username, pwd, db)

    def test_connection(self):
        '''Connects to the database then closes the connection.'''
        pass

    def load_records(self, record_list, source):
        '''Given a list of records, and the DN of the sender,
        loads them into the database.'''
        pass

    def get_records(self, record_class, query=None):
        '''
        Returns records from database with given record type.
        Query object specifies which rows from database should be loaded.
        '''
        pass


class Query(object):
    '''
    Class for representing queries to DB.

    Simple usage:
    we want to select JobRecords with EventTime > 2011-01-01.
    We must construct query as follows:
    query = Query()
    query.EventTime_gt = parseTimestamp('2011-01-01', '%Y-%m-%d')

    apel_db.get_records(... query, ...)
    '''
    def get_where(self):
        '''
        Returns dynamically created SQL conditions for query.

        For example: field_name > 0 AND field_name < 100
        '''
        parts = self._get_where_helper()
        if not parts:
            return ""
        clauses = len(parts) > 1 and ' AND '.join(parts) or parts[0]
        return " WHERE " + clauses

    def _get_where_helper(self):
        '''
        Private function which uses reflection to get information
        about fields and relations.

        Possible relations are:
        <, >, <=, >=, =

        Exaple usage:
        (Python code -> Sql code)
        query.Field_lt = 0 -> Field < 0
        query.Field_gt = 13 -> Field > 13
        query.Field_le = 10 -> Field <= 10
        query.Field_ge = 12 -> Field >= 12
        query.Field=7 -> Field = 7
        '''
        elems = []

        RELATIONS = {'lt'   : ' < ',
                     'gt'   : ' > ',
                     'le'   : ' <= ',
                     'ge'   : ' >= ',
                     'in'   : ' in ',
                     'notin': ' not in '}

        for elem in self.__dict__:
            if elem.endswith("_in"):
                column = elem[:-3]
                wh = '('
                for item in self.__dict__[elem]:
                    wh += '"' + item + '",'

                wh = wh[:-1] + ')'

                elems.append(column + ' IN ' + wh)
            elif elem.endswith("_notin"):
                column = elem[:-6]
                wh = '('
                for item in self.__dict__[elem]:
                    wh += '"' + item + '",'

                wh = wh[:-1] + ')'

                elems.append(column + ' NOT IN ' + wh)
            elif '_' in elem:
                column, relation = elem.split('_')
                if relation not in RELATIONS:
                    raise ApelDbException('Unknown relation: %s' % relation)
                elems.append( column + RELATIONS[relation] + "'" + str(self.__dict__[elem]) + "'" )
            else:
                elems.append(elem + ' = ' + "'" + str(self.__dict__[elem]) + "'")

        return elems