Skip to content

Commit

Permalink
Move Data Warehouse files to a separate folder. Refactor.
Browse files Browse the repository at this point in the history
  • Loading branch information
Dawid Makar committed Mar 13, 2024
1 parent 84e9a8e commit a39df4c
Show file tree
Hide file tree
Showing 9 changed files with 79 additions and 86 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ dwh = Dwh(SQL_SERVER, SQL_DATABASE, SQL_USER, SQL_PASSWORD, driver_index)
## Example usage

```
from pyprediktorutilities.dwh import Dwh
from pyprediktorutilities.dwh.dwh import Dwh
dwh = Dwh("localhost", "mydatabase", "myusername", "mypassword")
results = dwh.fetch("SELECT * FROM mytable")
Expand Down
Empty file.
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@
from typing import List, Any
from pydantic import validate_call

from pyprediktorutilities import singleton_class

logger = logging.getLogger(__name__)
logger.addHandler(logging.NullHandler())

Expand Down Expand Up @@ -304,11 +302,3 @@ def __disconnect(self) -> None:
def __commit(self) -> None:
"""Commits any changes to the database."""
self.connection.commit()


class DwhSingleton(Dwh, metaclass=singleton_class.SingletonMeta):
pass


def get_dwh_instance(*args, **kwargs):
return DwhSingleton(*args, **kwargs)
6 changes: 6 additions & 0 deletions src/pyprediktorutilities/dwh/dwh_singleton.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from pyprediktorutilities import singleton
from pyprediktorutilities.dwh import dwh


class DwhSingleton(dwh.Dwh, metaclass=singleton.SingletonMeta):
pass
File renamed without changes.
25 changes: 25 additions & 0 deletions tests/helpers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import random
import string

import pyodbc


class mock_pyodbc_connection:
def __init__(self, connection_string):
pass

def cursor(self):
return


def mock_pyodbc_connection_throws_error_not_tolerant_to_attempts(connection_string):
raise pyodbc.DataError("Error code", "Error message")


def mock_pyodbc_connection_throws_error_tolerant_to_attempts(connection_string):
raise pyodbc.DatabaseError("Error code", "Error message")


def grs():
"""Generate a random string."""
return "".join(random.choices(string.ascii_uppercase + string.digits, k=10))
101 changes: 27 additions & 74 deletions tests/test_dwh.py
Original file line number Diff line number Diff line change
@@ -1,54 +1,24 @@
import pytest
import random
import string
import pyodbc
import logging
import pandas as pd

from unittest.mock import Mock
from pyprediktorutilities.dwh import Dwh, get_dwh_instance
from pandas.testing import assert_frame_equal

"""
Helpers
"""


class mock_pyodbc_connection:
def __init__(self, connection_string):
pass

def cursor(self):
return


def mock_pyodbc_connection_throws_error_not_tolerant_to_attempts(connection_string):
raise pyodbc.DataError("Error code", "Error message")


def mock_pyodbc_connection_throws_error_tolerant_to_attempts(connection_string):
raise pyodbc.DatabaseError("Error code", "Error message")


def grs():
"""Generate a random string."""
return "".join(random.choices(string.ascii_uppercase + string.digits, k=10))

import pandas as pd
import pyodbc
import pytest
from pandas.testing import assert_frame_equal

"""
__init__
"""
import helpers
from pyprediktorutilities.dwh.dwh import Dwh


def test_init_when_instantiate_db_then_instance_is_created(monkeypatch):
driver_index = 0

# Mock the database connection
monkeypatch.setattr(
"pyprediktorutilities.dwh.pyodbc.connect", mock_pyodbc_connection
"pyprediktorutilities.dwh.dwh.pyodbc.connect", helpers.mock_pyodbc_connection
)

db = Dwh(grs(), grs(), grs(), grs(), driver_index)
db = Dwh(helpers.grs(), helpers.grs(), helpers.grs(), helpers.grs(), driver_index)
assert db is not None


Expand All @@ -58,10 +28,10 @@ def test_init_when_instantiate_db_but_no_pyodbc_drivers_available_then_throw_exc
driver_index = 0

# Mock the absence of ODBC drivers
monkeypatch.setattr("pyprediktorutilities.dwh.pyodbc.drivers", lambda: [])
monkeypatch.setattr("pyprediktorutilities.dwh.dwh.pyodbc.drivers", lambda: [])

with pytest.raises(ValueError) as excinfo:
Dwh(grs(), grs(), grs(), grs(), driver_index)
Dwh(helpers.grs(), helpers.grs(), helpers.grs(), helpers.grs(), driver_index)
assert "Driver index 0 is out of range." in str(excinfo.value)


Expand All @@ -72,12 +42,12 @@ def test_init_when_instantiate_db_but_pyodbc_throws_error_with_tolerance_to_atte

# Mock the database connection
monkeypatch.setattr(
"pyprediktorutilities.dwh.pyodbc.connect",
mock_pyodbc_connection_throws_error_not_tolerant_to_attempts,
"pyprediktorutilities.dwh.dwh.pyodbc.connect",
helpers.mock_pyodbc_connection_throws_error_not_tolerant_to_attempts,
)

with pytest.raises(pyodbc.DataError):
Dwh(grs(), grs(), grs(), grs(), driver_index)
Dwh(helpers.grs(), helpers.grs(), helpers.grs(), helpers.grs(), driver_index)


def test_init_when_instantiate_db_but_pyodbc_throws_error_tolerant_to_attempts_then_retry_connecting_and_throw_exception(
Expand All @@ -87,13 +57,13 @@ def test_init_when_instantiate_db_but_pyodbc_throws_error_tolerant_to_attempts_t

# Mock the database connection
monkeypatch.setattr(
"pyprediktorutilities.dwh.pyodbc.connect",
mock_pyodbc_connection_throws_error_tolerant_to_attempts,
"pyprediktorutilities.dwh.dwh.pyodbc.connect",
helpers.mock_pyodbc_connection_throws_error_tolerant_to_attempts,
)

with caplog.at_level(logging.ERROR):
with pytest.raises(pyodbc.DatabaseError):
Dwh(grs(), grs(), grs(), grs(), driver_index)
Dwh(helpers.grs(), helpers.grs(), helpers.grs(), helpers.grs(), driver_index)

assert any(
"Failed to connect to the DataWarehouse after 3 attempts." in message
Expand All @@ -111,7 +81,7 @@ def test_init_when_instantiate_dwh_but_driver_index_is_not_passed_then_instance_
monkeypatch.setattr("pyodbc.connect", lambda *args, **kwargs: mock_connection)
monkeypatch.setattr("pyodbc.drivers", lambda: ["Driver1", "Driver2"])

db = Dwh(grs(), grs(), grs(), grs())
db = Dwh(helpers.grs(), helpers.grs(), helpers.grs(), helpers.grs())
assert db is not None
assert db.driver == "Driver1"

Expand Down Expand Up @@ -145,7 +115,7 @@ def test_fetch_when_init_db_connection_is_successfull_but_fails_when_calling_fet
)

with pytest.raises(pyodbc.DataError):
db = Dwh(grs(), grs(), grs(), grs(), driver_index)
db = Dwh(helpers.grs(), helpers.grs(), helpers.grs(), helpers.grs(), driver_index)
db.connection = False
db.fetch(query)

Expand Down Expand Up @@ -178,7 +148,7 @@ def test_fetch_when_to_dataframe_is_false_and_no_data_is_returned_then_return_em
monkeypatch.setattr("pyodbc.connect", lambda *args, **kwargs: mock_connection)
monkeypatch.setattr("pyodbc.drivers", lambda: ["Driver1", "Driver2", "Driver3"])

db = Dwh(grs(), grs(), grs(), grs(), driver_index)
db = Dwh(helpers.grs(), helpers.grs(), helpers.grs(), helpers.grs(), driver_index)
actual_result = db.fetch(query)

mock_cursor.execute.assert_called_once_with(query)
Expand Down Expand Up @@ -270,7 +240,7 @@ def test_fetch_when_to_dataframe_is_false_and_single_data_set_is_returned_then_r
monkeypatch.setattr("pyodbc.connect", lambda *args, **kwargs: mock_connection)
monkeypatch.setattr("pyodbc.drivers", lambda: ["Driver1", "Driver2", "Driver3"])

db = Dwh(grs(), grs(), grs(), grs(), driver_index)
db = Dwh(helpers.grs(), helpers.grs(), helpers.grs(), helpers.grs(), driver_index)
actual_result = db.fetch(query)

mock_cursor.execute.assert_called_once_with(query)
Expand Down Expand Up @@ -407,7 +377,7 @@ def test_fetch_when_to_dataframe_is_false_and_multiple_data_sets_are_returned_th
monkeypatch.setattr("pyodbc.connect", lambda *args, **kwargs: mock_connection)
monkeypatch.setattr("pyodbc.drivers", lambda: ["Driver1", "Driver2", "Driver3"])

db = Dwh(grs(), grs(), grs(), grs(), driver_index)
db = Dwh(helpers.grs(), helpers.grs(), helpers.grs(), helpers.grs(), driver_index)
actual_result = db.fetch(query)

mock_cursor.execute.assert_called_once_with(query)
Expand Down Expand Up @@ -440,7 +410,7 @@ def test_fetch_when_to_dataframe_is_true_and_no_data_is_returned_then_return_emp
monkeypatch.setattr("pyodbc.connect", lambda *args, **kwargs: mock_connection)
monkeypatch.setattr("pyodbc.drivers", lambda: ["Driver1", "Driver2", "Driver3"])

db = Dwh(grs(), grs(), grs(), grs(), driver_index)
db = Dwh(helpers.grs(), helpers.grs(), helpers.grs(), helpers.grs(), driver_index)
actual_result = db.fetch(query, True)

mock_cursor.execute.assert_called_once_with(query)
Expand Down Expand Up @@ -533,7 +503,7 @@ def test_fetch_when_to_dataframe_is_true_and_single_data_set_is_returned_then_re
monkeypatch.setattr("pyodbc.connect", lambda *args, **kwargs: mock_connection)
monkeypatch.setattr("pyodbc.drivers", lambda: ["Driver1", "Driver2", "Driver3"])

db = Dwh(grs(), grs(), grs(), grs(), driver_index)
db = Dwh(helpers.grs(), helpers.grs(), helpers.grs(), helpers.grs(), driver_index)
actual_result = db.fetch(query, True)

mock_cursor.execute.assert_called_once_with(query)
Expand Down Expand Up @@ -674,7 +644,7 @@ def test_fetch_when_to_dataframe_is_true_and_multiple_data_sets_are_returned_the
monkeypatch.setattr("pyodbc.connect", lambda *args, **kwargs: mock_connection)
monkeypatch.setattr("pyodbc.drivers", lambda: ["Driver1", "Driver2", "Driver3"])

db = Dwh(grs(), grs(), grs(), grs(), driver_index)
db = Dwh(helpers.grs(), helpers.grs(), helpers.grs(), helpers.grs(), driver_index)
actual_result = db.fetch(query, True)

mock_cursor.execute.assert_called_once_with(query)
Expand Down Expand Up @@ -719,7 +689,7 @@ def test_execute_when_init_db_connection_is_successfull_but_fails_when_calling_e
)

with pytest.raises(pyodbc.DataError):
db = Dwh(grs(), grs(), grs(), grs(), driver_index)
db = Dwh(helpers.grs(), helpers.grs(), helpers.grs(), helpers.grs(), driver_index)
db.connection = False
db.execute(query)

Expand Down Expand Up @@ -749,7 +719,7 @@ def test_execute_when_parameter_passed_then_fetch_results_and_return_data(monkey
mock_cursor.execute = mock_execute
mock_cursor.fetchall = mock_fetch

db = Dwh(grs(), grs(), grs(), grs(), driver_index)
db = Dwh(helpers.grs(), helpers.grs(), helpers.grs(), helpers.grs(), driver_index)
actual_result = db.execute(query, param_one, param_two)

mock_execute.assert_called_once_with(query, param_one, param_two)
Expand Down Expand Up @@ -781,26 +751,9 @@ def test_execute_when_fetchall_throws_error_then_return_empty_list(monkeypatch):
mock_cursor.execute = mock_execute
mock_cursor.fetchall = mock_fetchall

db = Dwh(grs(), grs(), grs(), grs(), driver_index)
db = Dwh(helpers.grs(), helpers.grs(), helpers.grs(), helpers.grs(), driver_index)
actual_result = db.execute(query, param_one, param_two)

mock_execute.assert_called_once_with(query, param_one, param_two)
mock_fetchall.assert_called_once()
assert actual_result == []


def test_dwh_singleton_can_be_created_only_once(monkeypatch):
driver_index = 0

# Mock the database connection
monkeypatch.setattr(
"pyprediktorutilities.dwh.pyodbc.connect", mock_pyodbc_connection
)

db = get_dwh_instance(grs(), grs(), grs(), grs(), driver_index)
db_instance_address = id(db)

db_2 = get_dwh_instance(grs(), grs(), grs(), grs(), driver_index)
db_2_instance_address = id(db_2)

assert db_instance_address == db_2_instance_address
19 changes: 19 additions & 0 deletions tests/test_dwh_singleton.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from helpers import grs, mock_pyodbc_connection
from pyprediktorutilities.dwh import dwh_singleton


def test_dwh_singleton_can_be_created_only_once(monkeypatch):
driver_index = 0

# Mock the database connection
monkeypatch.setattr(
"pyprediktorutilities.dwh.dwh.pyodbc.connect", mock_pyodbc_connection
)

db = dwh_singleton.DwhSingleton(grs(), grs(), grs(), grs(), driver_index)
db_instance_address = id(db)

db_2 = dwh_singleton.DwhSingleton(grs(), grs(), grs(), grs(), driver_index)
db_2_instance_address = id(db_2)

assert db_instance_address == db_2_instance_address
2 changes: 1 addition & 1 deletion tests/test_shared.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import unittest
from unittest import mock
import requests
import pytest
from pydantic import ValidationError

Expand All @@ -17,6 +16,7 @@
}
]


# This method will be used by the mock to replace requests
def mocked_requests(*args, **kwargs):
class MockResponse:
Expand Down

0 comments on commit a39df4c

Please sign in to comment.