Commit f2c32f5e authored by Grant, Josh's avatar Grant, Josh
Browse files

WIP for mixin control

parent 74008878
Loading
Loading
Loading
Loading
+44 −57
Original line number Diff line number Diff line
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
    This class is responsible for interacting with the EAGLE-I database.
This class provides an abstract method of interacting with a (by default)
PostgreSQL database. However, the connection paramater may be specified to open
any type of connection through implementation of this abstract class.
"""
import traceback
from abc import ABC
import psycopg2
import pandas as pd
from abc import ABC, abstractmethod
from common.logz import create_logger
from common.env import check_environment as ce

@@ -44,6 +45,12 @@ class Database(ABC):
    DEFAULT_HOST = ce('DATABASE_HOST', "localhost")
    DEFAULT_PORT = int(ce('DATABASE_PORT', 5432))
    DEFAULT_SCHEMA = ce('DATABASE_SCHEMA', 'public')
    DEFAULT_ENGINE = ce('DATABASE_ENGINE', 'postgresql')
    # define a URI string if URI is perferred to connect
    DEFAULT_URI = (DEFAULT_ENGINE + '://' +
                   DEFAULT_USER + ':' + str(DEFAULT_PW) + '@' +
                   DEFAULT_HOST + '/' + DEFAULT_DB)


    def __init__(self, connection_info={'dbName': DEFAULT_DB,
                                        'dbUser': DEFAULT_USER,
@@ -51,21 +58,18 @@ class Database(ABC):
                                        'dbSchema': DEFAULT_SCHEMA,
                                        'dbHost': DEFAULT_HOST,
                                        'dbPort': DEFAULT_PORT,
                                        'dbTimeout': DEFAULT_TIMEOUT}
                 ):
                                        'dbTimeout': DEFAULT_TIMEOUT,
                                        'dbEngine': DEFAULT_ENGINE,
                                        'uri': DEFAULT_URI}, **kwargs):
        """ Create a Database object.  This *does not* open a connection to the
        database.  Use open() to establish a database connection.
            database.  Use open() or `with` to establish a database connection.

            :param connection_info: a dictionary containing connection info
        """
        self.logger = create_logger()
        self.dbName = connection_info['dbName']
        self.user = connection_info['dbUser']
        self.pw = connection_info['dbPassword']
        self.schema = connection_info['dbSchema']
        self.host = connection_info['dbHost']
        self.port = connection_info['dbPort']
        self.timeout = connection_info['dbTimeout']
        self._connection = None
        self._cursor = None
        self.conection_info = connection_info
        self.connection = None
        self.cursor = None
        self.logger.debug('Database object successfully initialized')

    def __del__(self):
@@ -89,40 +93,9 @@ class Database(ABC):
        """ hande database closing when leaving with """
        self.close()

    @abstractmethod
    def open(self):
        """ Explicitly open the database connection

        :note: This will send email to Database.CONNECTION_ERR_EMAIL_RECPS if
               unable to establish connection

        :note: Added as part of changes for EI-2649

        :return: True if connection established, else false
        """
        self.logger.debug('Opening Database Connection and creating Cursor')
        try:
            """ print(self.dbName + ',' + self.user + ',' + self.host + "," +
            str(self.port) + "," + str(self.timeout)) """
            self._connection = psycopg2.connect(database=self.dbName,
                                                user=self.user,
                                                password=self.pw,
                                                host=self.host,
                                                port=self.port,
                                                connect_timeout=self.timeout)
            self._connection.set_client_encoding('UTF8')
            self.logger.debug('Successfully opened connection to database')
            self._cursor = self._connection.cursor()
            self.logger.debug('Successfully created a cursor')
            # TODO @Jonathan v--- not the default search paths - env var?
            """ ^---- need this re-explained """
            self._cursor.execute("""SET search_path TO public, outage_data,
                                 utility, system_monitoring;""")
            self.logger.debug('Successfully set search_path')
        except psycopg2.OperationalError as e:  # TODO @Jonathan other errors?
            self.logger.error(f'Database Error: {e}')
            return False

        return True
        pass

    def silent_open(self):
        """Open database silently without returning anything."""
@@ -144,19 +117,19 @@ class Database(ABC):

        :return: None
        """
        if self._cursor:
            self._cursor.close()
            self._cursor = None
        if self.cursor:
            self.cursor.close()
            self.cursor = None

        if self._connection:
            self._connection.commit()
            self._connection.close()
            self._connection = None
        if self.connection:
            self.connection.commit()
            self.connection.close()
            self.connection = None

    def is_open(self):
        """Determine if the database is open or not."""
        # TODO @Jonathan -- code review please
        if self._cursor is not None and self._connection is not None:
        if self.cursor is not None and self._connection is not None:
            # Test if both connection and cursor are on
            return True  # If both are on, return True
        # If one or both connection and cursor are missing, return False
@@ -184,6 +157,20 @@ class Database(ABC):
            self.close()
        return results

    def override_connection(self, connection):
        """Override the default connection, useful for using other connections
           than `psycopg2` for downstream development.

           :param connection: any type of database connection object.
        """
        self.connection = connection

    def modify_connection_info(self, variable, value):
        """Modify a `variable` and set it to `value` in the connection_info
           attribute.
        """
        self.connection_info[variable] = value

    def query(self, query):
        """Query the database."""
        # TODO @Jonathan -- add logic here to send a query to the database
+3 −0
Original line number Diff line number Diff line
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""Provide mixins for multiple abstract classes."""
+43 −0
Original line number Diff line number Diff line
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""Allow opening with a psycopg2 connection."""
import psycopg2


class Postgres_Mixin():
    """Serve common connection method for postgres."""
    def open(self, search_paths='public'):
        """ Explicitly open the database connection

        :note: This will send email to Database.CONNECTION_ERR_EMAIL_RECPS if
               unable to establish connection

        :return: True if connection established, else false
        """
        self.logger.debug('Opening Database Connection and creating Cursor')
        try:
            """ print(self.dbName + ',' + self.user + ',' + self.host + "," +
            str(self.port) + "," + str(self.timeout)) """
            self.connection = psycopg2.connect(
                database=self.connection_info['dbName'],
                user=self.connection_info['dbUser'],
                password=self.connection_info['dbPassword'],
                host=self.connection_info['dbHost'],
                port=self.connection_info['dbPort'],
                connect_timeout=self.connection_info['dbTimeout'])
            self.connection.set_client_encoding('UTF8')
            self.logger.debug('Successfully opened connection to database')
            self.cursor = self._connection.cursor()
            self.logger.debug('Successfully created a cursor')
            # TODO @Jonathan v--- not the default search paths - env var?
            """ ^---- need this re-explained """
            # @TODO @Jonathan add searth_paths as a possible variable to pass
            # if passed as a list, split and concat into commas
            self.cursor.execute("""SET search_path TO public, outage_data,
                                 utility, system_monitoring;""")
            self.logger.debug('Successfully set search_path')
        except psycopg2.OperationalError as e:  # TODO @Jonathan other errors?
            self.logger.error(f'Database Error: {e}')
            return False

        return True
+1 −1
Original line number Diff line number Diff line
#!/usr/bin/env bash
python3 -m pip --no-cache-dir install common --extra-index-url https://__token__:dzK386VBsiR-iV8g2Ekc@code.ornl.gov/api/v4/projects/9965/packages/pypi/simple
python3 -m pip --no-cache-dir install common --index-url https://Install:dzK386VBsiR-iV8g2Ekc@code.ornl.gov/api/v4/projects/9965/packages/pypi/simple
+2 −2
Original line number Diff line number Diff line
@@ -5,10 +5,10 @@ import os
from setuptools import setup

# update version information here
_version = '0.1.0'
_version = '0.1.4'

# packages
_packages = ['common']
_packages = ['common', 'common.mixins']

# retrieve metadata and packages based on files
_here = os.path.abspath(os.path.dirname(__file__))