From 762f7314b6cdfed0c9f7c856563cf461417dd77b Mon Sep 17 00:00:00 2001 From: Alejandro Campillo Date: Mon, 12 Apr 2021 08:05:40 -0500 Subject: [PATCH 01/42] add: requirements file --- main.py | 2 +- requirements.txt | 31 +++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 requirements.txt diff --git a/main.py b/main.py index f7e29fe..b0374a7 100644 --- a/main.py +++ b/main.py @@ -8,4 +8,4 @@ port=int(settings['PORT']), reload=True, debug=True, workers=1 - ) + ) \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..268909d --- /dev/null +++ b/requirements.txt @@ -0,0 +1,31 @@ +# +# These requirements were autogenerated by pipenv +# To regenerate from the project's Pipfile, run: +# +# pipenv lock --requirements --dev +# + +# Note: in pipenv 2020.x, "--dev" changed to emit both default and development +# requirements. To emit only development requirements, pass "--dev-only". + +-i https://pypi.org/simple +attrs==20.3.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3' +click==7.1.2 +fastapi==0.63.0 +flake8==3.9.0 +h11==0.12.0; python_version >= '3.6' +iniconfig==1.1.1 +mccabe==0.6.1 +packaging==20.9; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3' +pluggy==0.13.1; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3' +py==1.10.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3' +pycodestyle==2.7.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3' +pydantic==1.8.1; python_full_version >= '3.6.1' +pyflakes==2.3.1; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3' +pyparsing==2.4.7; python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3' +pytest==6.2.3 +python-dotenv==0.17.0 +starlette==0.13.6; python_version >= '3.6' +toml==0.10.2; python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3' +typing-extensions==3.7.4.3 +uvicorn==0.13.4 \ No newline at end of file From e703c3b98abf5da0c46d497119e201cac41f483c Mon Sep 17 00:00:00 2001 From: Alejandro Campillo Date: Mon, 12 Apr 2021 17:16:05 -0500 Subject: [PATCH 02/42] Build: collection cmodels --- main.py | 5 +++- requirements.txt | 48 ++++++++++++++---------------- src/db/mongo.py | 18 ++++++++++- src/interfaces/cmodel_interface.py | 26 ++++++++++++++-- src/models/cmodel.py | 3 ++ tests/db/__init__.py | 0 tests/db/test_mongo.py | 25 ++++++++++++++++ 7 files changed, 95 insertions(+), 30 deletions(-) create mode 100644 tests/db/__init__.py create mode 100644 tests/db/test_mongo.py diff --git a/main.py b/main.py index 9a4e19b..2dff867 100644 --- a/main.py +++ b/main.py @@ -1,8 +1,11 @@ import uvicorn from src.config import settings +from src.interfaces.cmodel_interface import CmodelInterface +from src.models.cmodel import AllCModels -if __name__ == '__main__': +if __name__ == '__main__': + CmodelInterface.created_cmodel_collection(AllCModels) uvicorn.run( "src.api:app", host=settings['HOST'], diff --git a/requirements.txt b/requirements.txt index 3425e3b..6f3089b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,37 +1,33 @@ -# -# These requirements were autogenerated by pipenv -# To regenerate from the project's Pipfile, run: -# -# pipenv lock --requirements --dev -# - -# Note: in pipenv 2020.x, "--dev" changed to emit both default and development -# requirements. To emit only development requirements, pass "--dev-only". - --i https://pypi.org/simple -attrs==20.3.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3' +atomicwrites==1.4.0 +attrs==20.3.0 +autopep8==1.5.6 certifi==2020.12.5 -chardet==4.0.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' -click==7.1.2; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' +chardet==4.0.0 +click==7.1.2 +colorama==0.4.4 fastapi==0.63.0 flake8==3.9.0 -h11==0.12.0; python_version >= '3.6' -idna==2.10; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3' +h11==0.12.0 +idna==2.10 iniconfig==1.1.1 mccabe==0.6.1 -packaging==20.9; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3' -pluggy==0.13.1; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3' -py==1.10.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3' -pycodestyle==2.7.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3' -pydantic==1.8.1; python_full_version >= '3.6.1' -pyflakes==2.3.1; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3' +mongomock==3.22.1 +packaging==20.9 +pluggy==0.13.1 +py==1.10.0 +pycodestyle==2.7.0 +pydantic==1.8.1 +pyflakes==2.3.1 pymongo==3.11.3 -pyparsing==2.4.7; python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3' +pyparsing==2.4.7 pytest==6.2.3 python-dotenv==0.17.0 requests==2.25.1 -starlette==0.13.6; python_version >= '3.6' -toml==0.10.2; python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3' +sentinels==1.0.0 +six==1.15.0 +starlette==0.13.6 +toml==0.10.2 typing-extensions==3.7.4.3 -urllib3==1.26.4; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4' +urllib3==1.26.4 uvicorn==0.13.4 +wincertstore==0.2 diff --git a/src/db/mongo.py b/src/db/mongo.py index e009c44..e7056fe 100644 --- a/src/db/mongo.py +++ b/src/db/mongo.py @@ -1,4 +1,5 @@ from pymongo import MongoClient +from pymongo.database import Database from src.config import db_config @@ -11,9 +12,24 @@ def get_db(): db_connection: MongoClient Object containing the db connection """ - db_connection = MongoClient(db_config['MONGO_URI']) + db_connection = MongoClient(db_config['MONGO_URI'])[db_config['MONGO_DB']] try: yield db_connection finally: db_connection.close() + + +def get_db_connection() -> Database: + """ + Create database connection to mongo engine + + Return + ---------- + MongoClient + Object containing the db connection + """ + mongo_uri = db_config.get("MONGO_URI") + mongo_db = db_config.get("MONGO_DB") + + return MongoClient(mongo_uri).get_database(mongo_db) diff --git a/src/interfaces/cmodel_interface.py b/src/interfaces/cmodel_interface.py index ce0deb7..bffb51c 100644 --- a/src/interfaces/cmodel_interface.py +++ b/src/interfaces/cmodel_interface.py @@ -1,5 +1,27 @@ -from src.db import get_db_connection +from src.db.mongo import get_db_connection def model_db(): - return get_db_connection().get_collection('cmodels') + return get_db_connection()['cmodels'] + + +class CmodelInterface: + + @staticmethod + def created_cmodel_collection(AllCModels): + """ + Check if the default compartmental models exists + and if not, create them within the cmodels collection + + Parameters + ---------- + AllCmodels: dict + The compartmental models that will be save in the database + + Return + ---------- + model: pymongo object + """ + if model_db().find_one({'_id': {"$exists": True}}): + return print("models Already exist") + return [model_db().insert_one(model) for model in AllCModels().models] diff --git a/src/models/cmodel.py b/src/models/cmodel.py index 65575c1..3342ee8 100644 --- a/src/models/cmodel.py +++ b/src/models/cmodel.py @@ -22,6 +22,7 @@ class CompartmentalModel(Enum): ``` """ sir = { + '_id': 'SIR'.encode('utf-8').hex(), 'name': 'SIR', 'state_variables': ['S', 'I', 'R'], 'state_variables_units': { @@ -37,6 +38,7 @@ class CompartmentalModel(Enum): } seir = { + '_id': 'SEIR'.encode('utf-8').hex(), 'name': 'SEIR', 'state_variables': ['S', 'E', 'I', 'R'], 'state_variables_units': { @@ -53,6 +55,7 @@ class CompartmentalModel(Enum): } seirv = { + '_id': 'SEIRV'.encode('utf-8').hex(), 'name': 'SEIRV', 'state_variables': ['S', 'E', 'I', 'R', 'V'], 'state_variables_units': { diff --git a/tests/db/__init__.py b/tests/db/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/db/test_mongo.py b/tests/db/test_mongo.py new file mode 100644 index 0000000..bf795df --- /dev/null +++ b/tests/db/test_mongo.py @@ -0,0 +1,25 @@ +from unittest import TestCase +from unittest.mock import patch, Mock + +from mongomock import patch as db_patch +from pymongo.database import Database + +from src.db.mongo import get_db_connection + + +def solve_path(path: str): + source = 'src.db.mongo' + return ".".join([source, path]) + + +class MongoTestCase(TestCase): + + @db_patch(servers=(('mongodb://mongodb.example.com', 27017),)) + @patch(solve_path('db_config')) + def test_get_db_connection(self, mock_config: Mock): + server = "mongodb://mongodb0.example.com:27017" + mock_config.get.side_effect = [server, "test_db"] + + db = get_db_connection() + + self.assertIsInstance(db, Database) From ed2b52022ace59545e2e13a49fbf583686db27cd Mon Sep 17 00:00:00 2001 From: Alejandro Campillo Date: Fri, 16 Apr 2021 08:31:08 -0500 Subject: [PATCH 03/42] Build: use cases cmodels --- main.py | 28 +++++++++++++++++----------- src/interfaces/cmodel_interface.py | 7 ++++++- src/models/db/cmodels.py | 1 + src/models/routers/cmodel.py | 11 ----------- src/use_cases/cmodels.py | 9 +++++++++ src/utils/date_time.py | 8 ++++++++ 6 files changed, 41 insertions(+), 23 deletions(-) create mode 100644 src/use_cases/cmodels.py create mode 100644 src/utils/date_time.py diff --git a/main.py b/main.py index d34f318..18ef6d1 100644 --- a/main.py +++ b/main.py @@ -5,22 +5,28 @@ from src.interfaces.cmodel_interface import CmodelInterface from src.models.routers.cmodel import AllCModels from src.models.db.cmodels import CModelInDB +from src.use_cases.cmodels import CmodelUseCases def insert_cmodels_document(): for model in AllCModels().models: - cmodel_db_base = dict({'_id': model.name.encode('utf-8').hex()}, **CModelInDB( - inserted_at=datetime.now(), - updated_at=datetime.now()).dict()) + cmodel_db_base = dict({'_id': model.name.encode('utf-8').hex()}, + **CModelInDB(inserted_at=datetime.now(), + updated_at=datetime.now()).dict()) model_in_db = dict(cmodel_db_base, **model.dict()) - verify_model = CmodelInterface.read_model({"name": model.name}) - - if verify_model: - if (dict(verify_model) == dict(model_in_db)): - print(f'{"model: "}{model.name}{"Already exist"}') - return CmodelInterface.update_model( - {'name': model.name}, {"$set": model_in_db}) - return CmodelInterface.insert_cmodel(model_in_db) + find_model = CmodelInterface.read_model({"name": model.name}) + if find_model: + model_to_compare = CmodelUseCases.model_information_in_db_to_compare( + find_model) + if (dict(model_to_compare) == dict(model)): + print(f'{"model: "}{model.name}{" Already exist"}') + else: + model_in_db.pop('inserted_at') + CmodelInterface.update_model( + {'name': model.name}, {"$set": model_in_db}) + print(f'{"model: "}{model.name}{" Updated"}') + else: + CmodelInterface.insert_cmodel(model_in_db) if __name__ == '__main__': diff --git a/src/interfaces/cmodel_interface.py b/src/interfaces/cmodel_interface.py index 276a897..0b0ae9b 100644 --- a/src/interfaces/cmodel_interface.py +++ b/src/interfaces/cmodel_interface.py @@ -32,12 +32,17 @@ def insert_cmodel(model): CmodelInterface.cmodels_coll.insert_one(model) def read_model(query): - + """ + Docstring + """ with CmodelInterface.db_connection as client: return CmodelInterface.cmodels_coll.find_one(query) def update_model(colection_name, model): + """ + Docstring + """ with CmodelInterface.db_connection as client: diff --git a/src/models/db/cmodels.py b/src/models/db/cmodels.py index d701f95..8e24f06 100644 --- a/src/models/db/cmodels.py +++ b/src/models/db/cmodels.py @@ -2,6 +2,7 @@ from datetime import datetime from src.models.routers.cmodel import AllCModels +from src.utils.date_time import DateTime class CModelInDB(BaseModel): diff --git a/src/models/routers/cmodel.py b/src/models/routers/cmodel.py index 759567f..3d412b8 100644 --- a/src/models/routers/cmodel.py +++ b/src/models/routers/cmodel.py @@ -91,14 +91,3 @@ class CModelInDB(BaseModel): inserted_at: datetime updated_at: datetime - - -model = CModelInDB( - _id=AllCModels().models[0].name, inserted_at=datetime.now(), updated_at=datetime.now()).dict() -model = dict( - {'_id': AllCModels().models[0].name.encode('utf-8').hex()}, **model) -""" -model_in_db = AllCModels().models[0].dict().update(CModelInDB( - _id=AllCModels().models[0].name.encode('utf-8').hex(), inserted_at=datetime.now(), updated_at=datetime.now()).dict()) -""" -#print(dict(model, **AllCModels().models[0].dict())) diff --git a/src/use_cases/cmodels.py b/src/use_cases/cmodels.py new file mode 100644 index 0000000..f22045f --- /dev/null +++ b/src/use_cases/cmodels.py @@ -0,0 +1,9 @@ +class CmodelUseCases: + + @staticmethod + def model_information_in_db_to_compare(model_in_db: dict) -> dict: + + model_in_db.pop('_id') + model_in_db.pop('inserted_at') + model_in_db.pop('updated_at') + return model_in_db diff --git a/src/utils/date_time.py b/src/utils/date_time.py new file mode 100644 index 0000000..4184541 --- /dev/null +++ b/src/utils/date_time.py @@ -0,0 +1,8 @@ +from datetime import datetime + + +class DateTime: + + @classmethod + def current_datetime(cls) -> datetime: + return datetime.utcnow() From 55cc2a1ced47051e0640dfcdec186bc6be21db9a Mon Sep 17 00:00:00 2001 From: Alejandro Campillo Date: Fri, 16 Apr 2021 09:38:39 -0500 Subject: [PATCH 04/42] fix:updated insert_cmodels_document --- main.py | 2 +- src/interfaces/cmodel_interface.py | 4 ++-- src/models/routers/cmodel.py | 1 + 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/main.py b/main.py index 18ef6d1..c9c1dc6 100644 --- a/main.py +++ b/main.py @@ -23,7 +23,7 @@ def insert_cmodels_document(): else: model_in_db.pop('inserted_at') CmodelInterface.update_model( - {'name': model.name}, {"$set": model_in_db}) + {'name': model.name}, model_in_db) print(f'{"model: "}{model.name}{" Updated"}') else: CmodelInterface.insert_cmodel(model_in_db) diff --git a/src/interfaces/cmodel_interface.py b/src/interfaces/cmodel_interface.py index 0b0ae9b..3378901 100644 --- a/src/interfaces/cmodel_interface.py +++ b/src/interfaces/cmodel_interface.py @@ -39,11 +39,11 @@ def read_model(query): return CmodelInterface.cmodels_coll.find_one(query) - def update_model(colection_name, model): + def update_model(query, model): """ Docstring """ with CmodelInterface.db_connection as client: - CmodelInterface.cmodels_coll.update_one(colection_name, model) + CmodelInterface.cmodels_coll.update_one(query, {"$set": model}) diff --git a/src/models/routers/cmodel.py b/src/models/routers/cmodel.py index 3d412b8..504ed9d 100644 --- a/src/models/routers/cmodel.py +++ b/src/models/routers/cmodel.py @@ -73,6 +73,7 @@ class CompartmentalModel(Enum): parameters_units={ 'a': 'units of a', 'b': 'units of b', + 'c': 'unit_test' }, ) From aada17796a085afd282b60edbf9aaa07549bccd4 Mon Sep 17 00:00:00 2001 From: Alejandro Campillo Date: Fri, 16 Apr 2021 09:40:05 -0500 Subject: [PATCH 05/42] fix: model cmodel --- src/models/routers/cmodel.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/models/routers/cmodel.py b/src/models/routers/cmodel.py index 504ed9d..3d412b8 100644 --- a/src/models/routers/cmodel.py +++ b/src/models/routers/cmodel.py @@ -73,7 +73,6 @@ class CompartmentalModel(Enum): parameters_units={ 'a': 'units of a', 'b': 'units of b', - 'c': 'unit_test' }, ) From 002e25dcab7456e368bd778072d58865b4a3ad7e Mon Sep 17 00:00:00 2001 From: Alejandro Campillo Date: Mon, 19 Apr 2021 08:20:02 -0500 Subject: [PATCH 06/42] fix: remove unnecessary imports --- src/models/db/cmodels.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/models/db/cmodels.py b/src/models/db/cmodels.py index 8e24f06..dd72d06 100644 --- a/src/models/db/cmodels.py +++ b/src/models/db/cmodels.py @@ -1,9 +1,6 @@ from pydantic import BaseModel from datetime import datetime -from src.models.routers.cmodel import AllCModels -from src.utils.date_time import DateTime - class CModelInDB(BaseModel): From 1abb89d96880846566256f2216191de35a533748 Mon Sep 17 00:00:00 2001 From: Alejandro Campillo Date: Mon, 19 Apr 2021 08:20:39 -0500 Subject: [PATCH 07/42] fix: remove unnecessary imports --- src/models/routers/cmodel.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/models/routers/cmodel.py b/src/models/routers/cmodel.py index 3d412b8..8bbc345 100644 --- a/src/models/routers/cmodel.py +++ b/src/models/routers/cmodel.py @@ -1,6 +1,5 @@ from enum import Enum from typing import Dict, List -from datetime import datetime from pydantic import BaseModel @@ -73,6 +72,7 @@ class CompartmentalModel(Enum): parameters_units={ 'a': 'units of a', 'b': 'units of b', + 'c': 'units_test' }, ) @@ -85,9 +85,3 @@ class AllCModels(BaseModel): models: List[CompartmentalModel] = [ model.value for model in CompartmentalModel ] - - -class CModelInDB(BaseModel): - - inserted_at: datetime - updated_at: datetime From 3bab5134828cce84233b97eb93435deffbcd0581 Mon Sep 17 00:00:00 2001 From: Alejandro Campillo Date: Mon, 19 Apr 2021 08:22:12 -0500 Subject: [PATCH 08/42] build: utils cmodels file and create id cmodels method in usecases --- main.py | 28 +--------------------------- src/use_cases/cmodels.py | 4 ++++ src/utils/cmodels.py | 28 ++++++++++++++++++++++++++++ 3 files changed, 33 insertions(+), 27 deletions(-) create mode 100644 src/utils/cmodels.py diff --git a/main.py b/main.py index c9c1dc6..26a408e 100644 --- a/main.py +++ b/main.py @@ -1,33 +1,7 @@ import uvicorn -from datetime import datetime from src.config import settings -from src.interfaces.cmodel_interface import CmodelInterface -from src.models.routers.cmodel import AllCModels -from src.models.db.cmodels import CModelInDB -from src.use_cases.cmodels import CmodelUseCases - - -def insert_cmodels_document(): - for model in AllCModels().models: - cmodel_db_base = dict({'_id': model.name.encode('utf-8').hex()}, - **CModelInDB(inserted_at=datetime.now(), - updated_at=datetime.now()).dict()) - model_in_db = dict(cmodel_db_base, **model.dict()) - find_model = CmodelInterface.read_model({"name": model.name}) - if find_model: - model_to_compare = CmodelUseCases.model_information_in_db_to_compare( - find_model) - if (dict(model_to_compare) == dict(model)): - print(f'{"model: "}{model.name}{" Already exist"}') - else: - model_in_db.pop('inserted_at') - CmodelInterface.update_model( - {'name': model.name}, model_in_db) - print(f'{"model: "}{model.name}{" Updated"}') - else: - CmodelInterface.insert_cmodel(model_in_db) - +from src.utils.cmodels import insert_cmodels_document if __name__ == '__main__': diff --git a/src/use_cases/cmodels.py b/src/use_cases/cmodels.py index f22045f..9ee07d2 100644 --- a/src/use_cases/cmodels.py +++ b/src/use_cases/cmodels.py @@ -7,3 +7,7 @@ def model_information_in_db_to_compare(model_in_db: dict) -> dict: model_in_db.pop('inserted_at') model_in_db.pop('updated_at') return model_in_db + + def create_id_cmodel(model_name: str) -> dict: + + return {'_id': model_name.encode('utf-8').hex()} diff --git a/src/utils/cmodels.py b/src/utils/cmodels.py new file mode 100644 index 0000000..260ab97 --- /dev/null +++ b/src/utils/cmodels.py @@ -0,0 +1,28 @@ +from datetime import datetime + +from src.interfaces.cmodel_interface import CmodelInterface +from src.models.routers.cmodel import AllCModels +from src.models.db.cmodels import CModelInDB +from src.use_cases.cmodels import CmodelUseCases + + +def insert_cmodels_document(): + + for model in AllCModels().models: + cmodel_db_base = dict(CmodelUseCases.create_id_cmodel(model.name), + **CModelInDB(inserted_at=datetime.now(), + updated_at=datetime.now()).dict()) + model_in_db = dict(cmodel_db_base, **model.dict()) + find_model = CmodelInterface.read_model({"name": model.name}) + if find_model: + model_to_compare = CmodelUseCases.model_information_in_db_to_compare( + find_model) + if (dict(model_to_compare) == dict(model)): + print(f'{"model: "}{model.name}{" Already exist"}') + else: + model_in_db.pop('inserted_at') + CmodelInterface.update_model( + {'name': model.name}, model_in_db) + print(f'{"model: "}{model.name}{" Updated"}') + else: + CmodelInterface.insert_cmodel(model_in_db) From 981e07da129b402528327e77354fe8c4f89ae6fd Mon Sep 17 00:00:00 2001 From: Alejandro Campillo Date: Mon, 19 Apr 2021 14:58:34 -0500 Subject: [PATCH 09/42] build: test of cmodels created --- src/interfaces/cmodel_interface.py | 58 +++++++++++++++++++--- tests/interfaces/test_cmodels_interface.py | 58 ++++++++++++++++++---- 2 files changed, 98 insertions(+), 18 deletions(-) diff --git a/src/interfaces/cmodel_interface.py b/src/interfaces/cmodel_interface.py index 3378901..6c611b5 100644 --- a/src/interfaces/cmodel_interface.py +++ b/src/interfaces/cmodel_interface.py @@ -28,22 +28,66 @@ def insert_cmodel(model): model: pymongo object """ - with CmodelInterface.db_connection as client: - CmodelInterface.cmodels_coll.insert_one(model) + with CmodelInterface.db_connection: + return CmodelInterface.cmodels_coll.insert_one(model) def read_model(query): """ - Docstring + Search for a specific model inside the database + + Parameters + ---------- + query: dict + Key pair associated to the user + + Return + ---------- + model: pymongo object + Object containing the results of the search """ - with CmodelInterface.db_connection as client: + with CmodelInterface.db_connection: return CmodelInterface.cmodels_coll.find_one(query) def update_model(query, model): """ - Docstring + Update model's status to active after verification + + Parameters + ---------- + data: dict + Updated model parameters + + query: str + Key pair associated to the user + + Return + ---------- + False: + If model has no information + False: + If is not possible to update the model's status + False: + If the id doesn't match the one associated to the data parameter + True: + If the model has valid data and its status can be updated """ - with CmodelInterface.db_connection as client: + with CmodelInterface.db_connection: + + if not bool(model): + return False + cmodel = CmodelInterface.cmodels_coll.find_one(query) + print(cmodel) + if cmodel: + update_model = CmodelInterface.cmodels_coll.update_one(query, { + "$set": model}) + if update_model: + return True + return False + + def delete_model(query): + + with CmodelInterface.db_connection: - CmodelInterface.cmodels_coll.update_one(query, {"$set": model}) + return CmodelInterface.cmodels_coll.delete_one(query) diff --git a/tests/interfaces/test_cmodels_interface.py b/tests/interfaces/test_cmodels_interface.py index 4f785ac..d3d8ade 100644 --- a/tests/interfaces/test_cmodels_interface.py +++ b/tests/interfaces/test_cmodels_interface.py @@ -2,7 +2,7 @@ from unittest.mock import patch, Mock import pymongo -from mongomock import patch as db_path, ObjectId +from mongomock import patch as db_path from pymongo.results import InsertOneResult from src.interfaces.cmodel_interface import CmodelInterface @@ -26,23 +26,59 @@ def tearDown(self): self.client.close() @patch(solve_path('get_db')) - def test_insert_cmodel_documents_ok(self, mock_db: Mock): + def test_insert_cmodel_ok(self, mock_db: Mock): mock_db.return_value = self.client, self.test_mock - model = [{"_id": "example"}] + model = {"_id": "example"} - result = CmodelInterface.insert_cmodels_documents(model) + result = CmodelInterface.insert_cmodel(model) self.assertIsNotNone(result) - self.assertIsInstance(result[0], InsertOneResult) - """ + self.assertIsInstance(result, InsertOneResult) + + @patch(solve_path('get_db')) + def test_read_cmodel_ok(self, mock_db: Mock): + mock_db.return_value = self.client, self.test_mock + query = {"_id": "example"} + + result = CmodelInterface.read_model(query) + + self.assertIsNotNone(result) + self.assertIsInstance(result, dict) + + @patch(solve_path('get_db')) + def test_read_cmodel_not_found(self, mock_db: Mock): + mock_db.return_value = self.client, self.test_mock + + result = CmodelInterface.read_model({"_id": 'test_model_example'}) + + self.assertIsNone(result) + + @patch(solve_path('get_db')) + def test_update_cmodel_state_ok(self, mock_db: Mock): + mock_db.return_value = self.client, self.test_mock + query = {"_id": "example"} + data = {'params': ['a', 'b', 'c']} + + result = CmodelInterface.update_model(query, data) + + self.assertTrue(result) + @patch(solve_path('get_db')) - def test_insert_cmodel_documents_false(self, mock_db: Mock): + def test_update_cmodel_state_fail(self, mock_db: Mock): mock_db.return_value = self.client, self.test_mock - model = [{"_id": "example"}] - self.test_client.insert_one(model[0]) + query = {'_id': 'test_example'} + data = {'params': ['a', 'b', 'c']} - result = CmodelInterface.insert_cmodels_documents(model) + result = CmodelInterface.update_model(query, data) self.assertFalse(result) - """ + + @patch(solve_path('get_db')) + def test_delete_cmodel(self, mock_db: Mock): + mock_db.return_value = self.client, self.test_mock + query = {"_id": "example"} + + result = CmodelInterface.delete_model(query) + + self.assertTrue(result) From 65b8443b284abe94b3bb97b268380bf7172c0897 Mon Sep 17 00:00:00 2001 From: Juan Esteban Aristizabal Date: Tue, 20 Apr 2021 10:13:33 -0500 Subject: [PATCH 10/42] fix: style --- src/interfaces/cmodel_interface.py | 91 ++++++++++------------ tests/interfaces/test_cmodels_interface.py | 1 - 2 files changed, 42 insertions(+), 50 deletions(-) diff --git a/src/interfaces/cmodel_interface.py b/src/interfaces/cmodel_interface.py index 6c611b5..ef6eab7 100644 --- a/src/interfaces/cmodel_interface.py +++ b/src/interfaces/cmodel_interface.py @@ -14,80 +14,73 @@ class CmodelInterface: @staticmethod def insert_cmodel(model): - """ - Check if the default compartmental models exists - and if not, create them within the cmodels collection + """Check if the default compartmental models exists. - Parameters - ---------- - AllCmodels: dict - The compartmental models that will be save in the database + Parameters + ---------- + model + The compartmental models that will be save in the database - Return - ---------- - model: pymongo object + Return + ---------- + model: pymongo object """ - with CmodelInterface.db_connection: return CmodelInterface.cmodels_coll.insert_one(model) + @staticmethod def read_model(query): - """ - Search for a specific model inside the database + """Search for a specific model inside the database. - Parameters - ---------- - query: dict - Key pair associated to the user + Parameters + ---------- + query: dict + Key pair associated to the user - Return - ---------- - model: pymongo object - Object containing the results of the search + Return + ---------- + model: pymongo object + Object containing the results of the search """ with CmodelInterface.db_connection: - return CmodelInterface.cmodels_coll.find_one(query) + @staticmethod def update_model(query, model): - """ - Update model's status to active after verification - - Parameters - ---------- - data: dict - Updated model parameters - - query: str - Key pair associated to the user - - Return - ---------- - False: - If model has no information - False: - If is not possible to update the model's status - False: - If the id doesn't match the one associated to the data parameter - True: - If the model has valid data and its status can be updated + """Update model's status to active after verification + + Parameters + ---------- + data: dict + Updated model parameters + + query: str + Key pair associated to the user + + Return + ---------- + False: + * If model has no information + * If is not possible to update the model's status + * If the id doesn't match the one associated to the data parameter + True: + If the model has valid data and its status can be updated """ with CmodelInterface.db_connection: - if not bool(model): return False cmodel = CmodelInterface.cmodels_coll.find_one(query) - print(cmodel) if cmodel: - update_model = CmodelInterface.cmodels_coll.update_one(query, { - "$set": model}) + update_model = CmodelInterface.cmodels_coll.update_one( + query, + {"$set": model}, + ) if update_model: return True return False + @staticmethod def delete_model(query): - with CmodelInterface.db_connection: - return CmodelInterface.cmodels_coll.delete_one(query) diff --git a/tests/interfaces/test_cmodels_interface.py b/tests/interfaces/test_cmodels_interface.py index d3d8ade..bee2f94 100644 --- a/tests/interfaces/test_cmodels_interface.py +++ b/tests/interfaces/test_cmodels_interface.py @@ -6,7 +6,6 @@ from pymongo.results import InsertOneResult from src.interfaces.cmodel_interface import CmodelInterface -from src.db.mongo import api_get_db_connection, get_db def solve_path(path: str): From 41a2a546223c4ed476ee5b2ac83e75d30ef8bba4 Mon Sep 17 00:00:00 2001 From: Juan Esteban Aristizabal Date: Tue, 20 Apr 2021 10:55:45 -0500 Subject: [PATCH 11/42] .gitignore: .DS_Store --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 08132d3..f847706 100644 --- a/.gitignore +++ b/.gitignore @@ -151,3 +151,6 @@ cython_debug/ # Experiments experiments/ + +# MacOS +.DS_Store \ No newline at end of file From 49a0f078594ed03aeaa5844f9637d44e39a29a26 Mon Sep 17 00:00:00 2001 From: Juan Esteban Aristizabal Date: Tue, 20 Apr 2021 10:57:48 -0500 Subject: [PATCH 12/42] refactor: src.interfaces.cmodel_interface.cmodel_collection -> src.db.mongo.get_collection --- src/db/mongo.py | 16 ++++++++++++++++ src/interfaces/cmodel_interface.py | 11 ++--------- tests/interfaces/test_cmodels_interface.py | 2 +- 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/src/db/mongo.py b/src/db/mongo.py index ebcf18e..bfb4eb9 100644 --- a/src/db/mongo.py +++ b/src/db/mongo.py @@ -1,6 +1,8 @@ from typing import Generator, Tuple + from pymongo import MongoClient from pymongo.database import Database +from pymongo.collection import Collection from src.config import db_config @@ -45,3 +47,17 @@ def get_db( db: Database = db_connection[db_name] return db_connection, db + + +def get_collection( + coll_name: str = db_config['CMODELS_COLL'] +) -> Tuple[MongoClient, Collection]: + """ + Returns + ------- + db_connection: pymongo.MongoClient + coll: pymongo.collection.Collection + """ + db_connection, db = get_db() + coll: Collection = db[coll_name] + return db_connection, coll diff --git a/src/interfaces/cmodel_interface.py b/src/interfaces/cmodel_interface.py index ef6eab7..acc65d5 100644 --- a/src/interfaces/cmodel_interface.py +++ b/src/interfaces/cmodel_interface.py @@ -1,16 +1,9 @@ -from src.db.mongo import get_db -from src.config import db_config - - -def cmodel_collection(): - db_connection, db = get_db() - cmodels_coll = db[db_config['CMODELS_COLL']] - return db_connection, cmodels_coll +from src.db.mongo import get_collection class CmodelInterface: - db_connection, cmodels_coll = cmodel_collection() + db_connection, cmodels_coll = get_collection() @staticmethod def insert_cmodel(model): diff --git a/tests/interfaces/test_cmodels_interface.py b/tests/interfaces/test_cmodels_interface.py index bee2f94..090c1fb 100644 --- a/tests/interfaces/test_cmodels_interface.py +++ b/tests/interfaces/test_cmodels_interface.py @@ -9,7 +9,7 @@ def solve_path(path: str): - source = 'src.interfaces.cmodel_interface' + source = 'src.db.mongo' return ".".join([source, path]) From 93e9c43c0920481ef657401a65190dec15335c93 Mon Sep 17 00:00:00 2001 From: Juan Esteban Aristizabal Date: Tue, 20 Apr 2021 12:33:05 -0500 Subject: [PATCH 13/42] refactor: crud --- src/interfaces/cmodel_interface.py | 79 --------------------- src/interfaces/crud.py | 82 ++++++++++++++++++++++ src/utils/cmodels.py | 8 +-- tests/interfaces/test_cmodels_interface.py | 66 +++++++---------- 4 files changed, 113 insertions(+), 122 deletions(-) delete mode 100644 src/interfaces/cmodel_interface.py create mode 100644 src/interfaces/crud.py diff --git a/src/interfaces/cmodel_interface.py b/src/interfaces/cmodel_interface.py deleted file mode 100644 index acc65d5..0000000 --- a/src/interfaces/cmodel_interface.py +++ /dev/null @@ -1,79 +0,0 @@ -from src.db.mongo import get_collection - - -class CmodelInterface: - - db_connection, cmodels_coll = get_collection() - - @staticmethod - def insert_cmodel(model): - """Check if the default compartmental models exists. - - Parameters - ---------- - model - The compartmental models that will be save in the database - - Return - ---------- - model: pymongo object - """ - with CmodelInterface.db_connection: - return CmodelInterface.cmodels_coll.insert_one(model) - - @staticmethod - def read_model(query): - """Search for a specific model inside the database. - - Parameters - ---------- - query: dict - Key pair associated to the user - - Return - ---------- - model: pymongo object - Object containing the results of the search - """ - with CmodelInterface.db_connection: - return CmodelInterface.cmodels_coll.find_one(query) - - @staticmethod - def update_model(query, model): - """Update model's status to active after verification - - Parameters - ---------- - data: dict - Updated model parameters - - query: str - Key pair associated to the user - - Return - ---------- - False: - * If model has no information - * If is not possible to update the model's status - * If the id doesn't match the one associated to the data parameter - True: - If the model has valid data and its status can be updated - """ - - with CmodelInterface.db_connection: - if not bool(model): - return False - cmodel = CmodelInterface.cmodels_coll.find_one(query) - if cmodel: - update_model = CmodelInterface.cmodels_coll.update_one( - query, - {"$set": model}, - ) - if update_model: - return True - return False - - @staticmethod - def delete_model(query): - with CmodelInterface.db_connection: - return CmodelInterface.cmodels_coll.delete_one(query) diff --git a/src/interfaces/crud.py b/src/interfaces/crud.py new file mode 100644 index 0000000..6399470 --- /dev/null +++ b/src/interfaces/crud.py @@ -0,0 +1,82 @@ +from typing import Any, Union + +from pymongo.collection import Collection +from pymongo.mongo_client import MongoClient + + +class MongoCRUD(): + def __init__( + self, + db_connection: MongoClient, + collection: Collection + ) -> None: + self.db_connection = db_connection + self.collection = collection + + def insert_cmodel(self, data: dict): + """Check if the default compartmental models exists. + + Parameters + ---------- + model + The compartmental models that will be save in the database + + Return + ---------- + model: pymongo object + """ + with self.db_connection: + return self.collection.insert_one(data) + + def read_model(self, query: dict) -> Union[Any, None]: + """Search for a specific model inside the database. + + Parameters + ---------- + query: dict + Key pair associated to the user + + Return + ---------- + model: pymongo object + Object containing the results of the search + """ + with self.db_connection: + return self.collection.find_one(query) + + def update_model(self, query: dict, new_data: dict) -> bool: + """Update model's status to active after verification + + Parameters + ---------- + query + Document's id. ``dict`` schema: {'_id': ``bson.ObjectID``} + data + Updated document fields + + Return + ---------- + False: + * If query has no information + * If is not possible to update the document's status + * If the query id doesn't match the one associated to ``new_data`` + True: + If the model has valid data and its status can be updated + """ + + with self.db_connection: + if not query: + return False + cmodel = self.collection.find_one(query) + if cmodel: + update_model = self.collection.update_one( + query, + {"$set": new_data}, + ) + if update_model: + return True + return False + + def delete_model(self, query): + with self.db_connection: + return self.collection.delete_one(query) diff --git a/src/utils/cmodels.py b/src/utils/cmodels.py index 260ab97..371699d 100644 --- a/src/utils/cmodels.py +++ b/src/utils/cmodels.py @@ -1,6 +1,6 @@ from datetime import datetime -from src.interfaces.cmodel_interface import CmodelInterface +from src.interfaces.crud import MongoCRUD from src.models.routers.cmodel import AllCModels from src.models.db.cmodels import CModelInDB from src.use_cases.cmodels import CmodelUseCases @@ -13,7 +13,7 @@ def insert_cmodels_document(): **CModelInDB(inserted_at=datetime.now(), updated_at=datetime.now()).dict()) model_in_db = dict(cmodel_db_base, **model.dict()) - find_model = CmodelInterface.read_model({"name": model.name}) + find_model = MongoCRUD.read_model({"name": model.name}) if find_model: model_to_compare = CmodelUseCases.model_information_in_db_to_compare( find_model) @@ -21,8 +21,8 @@ def insert_cmodels_document(): print(f'{"model: "}{model.name}{" Already exist"}') else: model_in_db.pop('inserted_at') - CmodelInterface.update_model( + MongoCRUD.update_model( {'name': model.name}, model_in_db) print(f'{"model: "}{model.name}{" Updated"}') else: - CmodelInterface.insert_cmodel(model_in_db) + MongoCRUD.insert_cmodel(model_in_db) diff --git a/tests/interfaces/test_cmodels_interface.py b/tests/interfaces/test_cmodels_interface.py index 090c1fb..f71a6d9 100644 --- a/tests/interfaces/test_cmodels_interface.py +++ b/tests/interfaces/test_cmodels_interface.py @@ -1,11 +1,11 @@ from unittest import TestCase from unittest.mock import patch, Mock -import pymongo +import mongomock from mongomock import patch as db_path from pymongo.results import InsertOneResult -from src.interfaces.cmodel_interface import CmodelInterface +from src.interfaces.crud import MongoCRUD def solve_path(path: str): @@ -14,70 +14,58 @@ def solve_path(path: str): class CmodelInterfaceTestCase(TestCase): + server = "mongodb://mongodb0.example.com:27017" - @db_path(servers=(('mongodb.example.com', 27017),)) + @db_path(servers=(('server.example.com', 27017),)) def setUp(self): - self.client = pymongo.MongoClient('mongodb.example.com') - self.test_mock = self.client.get_database('test') - self.test_collection = self.test_mock.get_collection('cmodels') + self.client = mongomock.MongoClient('server.example.com') + self.test_mock = self.client.db + self.test_collection = self.test_mock.collection + self.mongo_crud = MongoCRUD(self.client, self.test_collection) def tearDown(self): self.client.close() @patch(solve_path('get_db')) - def test_insert_cmodel_ok(self, mock_db: Mock): - mock_db.return_value = self.client, self.test_mock - + def test_insert_cmodel_ok(self, mock: Mock): model = {"_id": "example"} - result = CmodelInterface.insert_cmodel(model) + result = self.mongo_crud.insert_cmodel(model) self.assertIsNotNone(result) self.assertIsInstance(result, InsertOneResult) @patch(solve_path('get_db')) - def test_read_cmodel_ok(self, mock_db: Mock): - mock_db.return_value = self.client, self.test_mock - query = {"_id": "example"} + def test_read_cmodel_ok(self, mock: Mock): + model = {"_id": "example"} - result = CmodelInterface.read_model(query) + self.mongo_crud.insert_cmodel(model) + read_result = self.mongo_crud.read_model(model) - self.assertIsNotNone(result) - self.assertIsInstance(result, dict) + self.assertIsNotNone(read_result) + self.assertIsInstance(read_result, dict) @patch(solve_path('get_db')) - def test_read_cmodel_not_found(self, mock_db: Mock): - mock_db.return_value = self.client, self.test_mock - - result = CmodelInterface.read_model({"_id": 'test_model_example'}) - + def test_read_cmodel_not_found(self, mock: Mock): + model = {"_id": "example"} + result = self.mongo_crud.read_model(model) self.assertIsNone(result) @patch(solve_path('get_db')) - def test_update_cmodel_state_ok(self, mock_db: Mock): - mock_db.return_value = self.client, self.test_mock - query = {"_id": "example"} - data = {'params': ['a', 'b', 'c']} + def test_update_cmodel_state_ok(self, mock: Mock): + model = {"_id": "example"} - result = CmodelInterface.update_model(query, data) + self.mongo_crud.insert_cmodel(model) + new_data = {'params': ['a', 'b', 'c']} + + result = self.mongo_crud.update_model(model, new_data) self.assertTrue(result) @patch(solve_path('get_db')) - def test_update_cmodel_state_fail(self, mock_db: Mock): - mock_db.return_value = self.client, self.test_mock + def test_update_cmodel_state_fail(self, mock: Mock): query = {'_id': 'test_example'} - data = {'params': ['a', 'b', 'c']} - result = CmodelInterface.update_model(query, data) + result = self.mongo_crud.update_model(query, {}) self.assertFalse(result) - - @patch(solve_path('get_db')) - def test_delete_cmodel(self, mock_db: Mock): - mock_db.return_value = self.client, self.test_mock - query = {"_id": "example"} - - result = CmodelInterface.delete_model(query) - - self.assertTrue(result) From 6a42b3601af3040709110ca5daf70e19ca49306b Mon Sep 17 00:00:00 2001 From: Juan Esteban Aristizabal Date: Wed, 21 Apr 2021 00:40:50 -0500 Subject: [PATCH 14/42] refactor: compartmentl models db-related stuff --- src/interfaces/crud.py | 23 +++-- src/models/db/__init__.py | 13 +++ src/models/db/cmodels.py | 94 ++++++++++++++++++- src/models/routers/__init__.py | 6 -- src/models/routers/cmodel.py | 87 ----------------- src/use_cases/cmodels.py | 4 - src/utils/cmodels.py | 50 ++++++---- tests/interfaces/test_cmodels_interface.py | 16 ++-- tests/models/db/__init__.py | 0 .../test_cmodel.py => db/test_cmodels.py} | 13 +-- 10 files changed, 163 insertions(+), 143 deletions(-) create mode 100644 tests/models/db/__init__.py rename tests/models/{routers/test_cmodel.py => db/test_cmodels.py} (68%) diff --git a/src/interfaces/crud.py b/src/interfaces/crud.py index 6399470..94ce9df 100644 --- a/src/interfaces/crud.py +++ b/src/interfaces/crud.py @@ -13,9 +13,8 @@ def __init__( self.db_connection = db_connection self.collection = collection - def insert_cmodel(self, data: dict): - """Check if the default compartmental models exists. - + def insert(self, document: dict): + """ Parameters ---------- model @@ -26,15 +25,15 @@ def insert_cmodel(self, data: dict): model: pymongo object """ with self.db_connection: - return self.collection.insert_one(data) + return self.collection.insert_one(document) - def read_model(self, query: dict) -> Union[Any, None]: - """Search for a specific model inside the database. + def read(self, query: dict) -> Union[Any, None]: + """Search for a specific model in ``self.collection``. Parameters ---------- - query: dict - Key pair associated to the user + query + Document's id. ``dict`` schema: ``{'_id': bson.ObjectID}`` Return ---------- @@ -44,13 +43,13 @@ def read_model(self, query: dict) -> Union[Any, None]: with self.db_connection: return self.collection.find_one(query) - def update_model(self, query: dict, new_data: dict) -> bool: - """Update model's status to active after verification + def update(self, query: dict, new_data: dict) -> bool: + """Update document in ``self.collection``. Parameters ---------- query - Document's id. ``dict`` schema: {'_id': ``bson.ObjectID``} + Document's id. ``dict`` schema: ``{'_id': bson.ObjectID}`` data Updated document fields @@ -77,6 +76,6 @@ def update_model(self, query: dict, new_data: dict) -> bool: return True return False - def delete_model(self, query): + def delete(self, query): with self.db_connection: return self.collection.delete_one(query) diff --git a/src/models/db/__init__.py b/src/models/db/__init__.py index e69de29..94764d2 100644 --- a/src/models/db/__init__.py +++ b/src/models/db/__init__.py @@ -0,0 +1,13 @@ +from .cmodels import ( + CompartmentalModelBase, + CompartmentalModel, + CModel, + AllCModels +) + +__all__ = [ + 'CompartmentalModelBase', + 'CompartmentalModel', + 'CModel', + 'AllCModels' +] diff --git a/src/models/db/cmodels.py b/src/models/db/cmodels.py index dd72d06..65c3a34 100644 --- a/src/models/db/cmodels.py +++ b/src/models/db/cmodels.py @@ -1,8 +1,98 @@ -from pydantic import BaseModel from datetime import datetime +from pydantic import BaseModel, Field +from enum import Enum +from typing import Dict, List -class CModelInDB(BaseModel): +class CompartmentalModelBase(BaseModel): + """Base Model for Compartmental Models + """ + name: str + """Name of the compartmental model""" + state_variables: List[str] + """Name of state variables of corresponding model""" + state_variables_units: Dict[str, str] + """Units of each state variable. The keys are the 'state_variables' array + elements + """ + parameters: List[str] + """Parameters of the corresponding model""" + parameters_units: Dict[str, str] + """Units of each parameter. The keys are the 'parameters' array elements""" + + +class CompartmentalModel(CompartmentalModelBase): + id: str = Field(..., alias='_id') inserted_at: datetime updated_at: datetime + + class Config: + allow_population_by_field_name = True + + +class CompartmentalModelEnum(Enum): + """Compartmental Models' Data. + + Each element of this ``Enum`` class contains all the essential information + on the corresponding Compartmental Model. Each element is a + :class:``CompartmentalModelBase`` object + """ + sir: CompartmentalModelBase = CompartmentalModelBase( + name='SIR', + state_variables=['S', 'I', 'R'], + state_variables_units={ + 'S': 'persons', + 'I': 'persons', + 'R': 'persons', + }, + parameters=['a', 'b'], + parameters_units={ + 'a': 'units of a', + 'b': 'units of b', + }, + ) + + seir: CompartmentalModelBase = CompartmentalModelBase( + name='SEIR', + state_variables=['S', 'E', 'I', 'R'], + state_variables_units={ + 'S': 'persons', + 'E': 'persons', + 'I': 'persons', + 'R': 'persons', + }, + parameters=['a', 'b'], + parameters_units={ + 'a': 'units of a', + 'b': 'units of b', + }, + ) + + seirv: CompartmentalModelBase = CompartmentalModelBase( + name='SEIRV', + state_variables=['S', 'E', 'I', 'R', 'V'], + state_variables_units={ + 'S': 'persons', + 'E': 'persons', + 'I': 'persons', + 'R': 'persons', + 'V': 'persons', + }, + parameters=['a', 'b'], + parameters_units={ + 'a': 'units of a', + 'b': 'units of b', + 'c': 'units_test' + }, + ) + + +class CModel(BaseModel): + model: CompartmentalModelEnum + + +class AllCModels(BaseModel): + models: List[CompartmentalModelEnum] = [ + model.value for model in CompartmentalModelEnum + ] diff --git a/src/models/routers/__init__.py b/src/models/routers/__init__.py index 0f2ba5f..e3625e0 100644 --- a/src/models/routers/__init__.py +++ b/src/models/routers/__init__.py @@ -1,9 +1,3 @@ -from .cmodel import ( - CompartmentalModelBase, - CompartmentalModel, - CModel, - AllCModels -) from .simulation import ( SimulationType, SimulationConfig diff --git a/src/models/routers/cmodel.py b/src/models/routers/cmodel.py index 8bbc345..e69de29 100644 --- a/src/models/routers/cmodel.py +++ b/src/models/routers/cmodel.py @@ -1,87 +0,0 @@ -from enum import Enum -from typing import Dict, List -from pydantic import BaseModel - - -class CompartmentalModelBase(BaseModel): - """Base Model for Compartmental Models - """ - name: str - """Name of the compartmental model""" - state_variables: List[str] - """Name of state variables of corresponding model""" - state_variables_units: Dict[str, str] - """Units of each state variable. The keys are the 'state_variables' array - elements - """ - parameters: List[str] - """Parameters of the corresponding model""" - parameters_units: Dict[str, str] - """Units of each parameter. The keys are the 'parameters' array elements""" - - -class CompartmentalModel(Enum): - """Compartmental Models' Data. - - Each element of this ``Enum`` class contains all the essential information - on the corresponding Compartmental Model. Each element is a - :class:``CompartmentalModelBase`` object - """ - sir = CompartmentalModelBase( - name='SIR', - state_variables=['S', 'I', 'R'], - state_variables_units={ - 'S': 'persons', - 'I': 'persons', - 'R': 'persons', - }, - parameters=['a', 'b'], - parameters_units={ - 'a': 'units of a', - 'b': 'units of b', - }, - ) - - seir = CompartmentalModelBase( - name='SEIR', - state_variables=['S', 'E', 'I', 'R'], - state_variables_units={ - 'S': 'persons', - 'E': 'persons', - 'I': 'persons', - 'R': 'persons', - }, - parameters=['a', 'b'], - parameters_units={ - 'a': 'units of a', - 'b': 'units of b', - }, - ) - - seirv = CompartmentalModelBase( - name='SEIRV', - state_variables=['S', 'E', 'I', 'R', 'V'], - state_variables_units={ - 'S': 'persons', - 'E': 'persons', - 'I': 'persons', - 'R': 'persons', - 'V': 'persons', - }, - parameters=['a', 'b'], - parameters_units={ - 'a': 'units of a', - 'b': 'units of b', - 'c': 'units_test' - }, - ) - - -class CModel(BaseModel): - model: CompartmentalModel - - -class AllCModels(BaseModel): - models: List[CompartmentalModel] = [ - model.value for model in CompartmentalModel - ] diff --git a/src/use_cases/cmodels.py b/src/use_cases/cmodels.py index 9ee07d2..f22045f 100644 --- a/src/use_cases/cmodels.py +++ b/src/use_cases/cmodels.py @@ -7,7 +7,3 @@ def model_information_in_db_to_compare(model_in_db: dict) -> dict: model_in_db.pop('inserted_at') model_in_db.pop('updated_at') return model_in_db - - def create_id_cmodel(model_name: str) -> dict: - - return {'_id': model_name.encode('utf-8').hex()} diff --git a/src/utils/cmodels.py b/src/utils/cmodels.py index 371699d..3d27c50 100644 --- a/src/utils/cmodels.py +++ b/src/utils/cmodels.py @@ -1,28 +1,42 @@ from datetime import datetime +from src.db.mongo import get_collection +from src.models.db.cmodels import ( + CompartmentalModelBase, + CompartmentalModelEnum +) +from src.models.db.cmodels import CompartmentalModel from src.interfaces.crud import MongoCRUD -from src.models.routers.cmodel import AllCModels -from src.models.db.cmodels import CModelInDB from src.use_cases.cmodels import CmodelUseCases def insert_cmodels_document(): - for model in AllCModels().models: - cmodel_db_base = dict(CmodelUseCases.create_id_cmodel(model.name), - **CModelInDB(inserted_at=datetime.now(), - updated_at=datetime.now()).dict()) - model_in_db = dict(cmodel_db_base, **model.dict()) - find_model = MongoCRUD.read_model({"name": model.name}) - if find_model: - model_to_compare = CmodelUseCases.model_information_in_db_to_compare( - find_model) - if (dict(model_to_compare) == dict(model)): - print(f'{"model: "}{model.name}{" Already exist"}') + for model in CompartmentalModelEnum: + model: CompartmentalModelBase = model.value + + cmodel_document = CompartmentalModel( + id=model.name.encode('utf-8').hex(), + inserted_at=datetime.now(), + updated_at=datetime.now(), + **model.dict() + ).dict(by_alias=True) + + cmodels_crud = MongoCRUD(*get_collection()) + + existent_model = cmodels_crud.read({"name": model.name}) + if existent_model: + pruned_existent_model = \ + CmodelUseCases.model_information_in_db_to_compare( + existent_model + ) + if pruned_existent_model == model.dict(): + # TODO: log cmodel exists + ... else: - model_in_db.pop('inserted_at') - MongoCRUD.update_model( - {'name': model.name}, model_in_db) - print(f'{"model: "}{model.name}{" Updated"}') + cmodel_document.pop('inserted_at') + cmodels_crud.update(cmodel_document) + # TODO log updated cmodel else: - MongoCRUD.insert_cmodel(model_in_db) + cmodels_crud.insert(cmodel_document) + # TODO: log created cmodel diff --git a/tests/interfaces/test_cmodels_interface.py b/tests/interfaces/test_cmodels_interface.py index f71a6d9..5e7d770 100644 --- a/tests/interfaces/test_cmodels_interface.py +++ b/tests/interfaces/test_cmodels_interface.py @@ -13,7 +13,7 @@ def solve_path(path: str): return ".".join([source, path]) -class CmodelInterfaceTestCase(TestCase): +class MongoCRUDTestCase(TestCase): server = "mongodb://mongodb0.example.com:27017" @db_path(servers=(('server.example.com', 27017),)) @@ -30,7 +30,7 @@ def tearDown(self): def test_insert_cmodel_ok(self, mock: Mock): model = {"_id": "example"} - result = self.mongo_crud.insert_cmodel(model) + result = self.mongo_crud.insert(model) self.assertIsNotNone(result) self.assertIsInstance(result, InsertOneResult) @@ -39,8 +39,8 @@ def test_insert_cmodel_ok(self, mock: Mock): def test_read_cmodel_ok(self, mock: Mock): model = {"_id": "example"} - self.mongo_crud.insert_cmodel(model) - read_result = self.mongo_crud.read_model(model) + self.mongo_crud.insert(model) + read_result = self.mongo_crud.read(model) self.assertIsNotNone(read_result) self.assertIsInstance(read_result, dict) @@ -48,17 +48,17 @@ def test_read_cmodel_ok(self, mock: Mock): @patch(solve_path('get_db')) def test_read_cmodel_not_found(self, mock: Mock): model = {"_id": "example"} - result = self.mongo_crud.read_model(model) + result = self.mongo_crud.read(model) self.assertIsNone(result) @patch(solve_path('get_db')) def test_update_cmodel_state_ok(self, mock: Mock): model = {"_id": "example"} - self.mongo_crud.insert_cmodel(model) + self.mongo_crud.insert(model) new_data = {'params': ['a', 'b', 'c']} - result = self.mongo_crud.update_model(model, new_data) + result = self.mongo_crud.update(model, new_data) self.assertTrue(result) @@ -66,6 +66,6 @@ def test_update_cmodel_state_ok(self, mock: Mock): def test_update_cmodel_state_fail(self, mock: Mock): query = {'_id': 'test_example'} - result = self.mongo_crud.update_model(query, {}) + result = self.mongo_crud.update(query, {}) self.assertFalse(result) diff --git a/tests/models/db/__init__.py b/tests/models/db/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/models/routers/test_cmodel.py b/tests/models/db/test_cmodels.py similarity index 68% rename from tests/models/routers/test_cmodel.py rename to tests/models/db/test_cmodels.py index 709c319..cfc938e 100644 --- a/tests/models/routers/test_cmodel.py +++ b/tests/models/db/test_cmodels.py @@ -1,8 +1,9 @@ +import pytest from hypothesis import given, strategies as st -from src.models.routers.cmodel import ( +from src.models.db.cmodels import ( CompartmentalModelBase, - CompartmentalModel, + CompartmentalModelEnum, CModel, AllCModels, ) @@ -17,14 +18,14 @@ def test_CompartmentalModelBase_properties(instance: CompartmentalModelBase): assert isinstance(instance.parameters_units, dict) -def test_CompartmentalModel(): - for model in CompartmentalModel: - assert isinstance(model.value, CompartmentalModelBase) +@pytest.mark.parametrize('model', [model for model in CompartmentalModelEnum]) +def test_CompartmentalModel(model: CompartmentalModelEnum): + assert isinstance(model.value, CompartmentalModelBase) @given(st.builds(CModel)) def test_CModel(instance: CModel): - assert isinstance(instance.model, CompartmentalModel) + assert isinstance(instance.model, CompartmentalModelEnum) @given(st.builds(AllCModels)) From 842d01171eff3d71e994ef370aecc4cddbe872f7 Mon Sep 17 00:00:00 2001 From: Juan Esteban Aristizabal Date: Wed, 21 Apr 2021 01:10:15 -0500 Subject: [PATCH 15/42] fix: sonnarcloud-reported bugs --- src/models/routers/__init__.py | 4 ---- src/use_cases/cmodels.py | 1 - src/utils/cmodels.py | 15 ++++++++++++--- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/models/routers/__init__.py b/src/models/routers/__init__.py index e3625e0..f56f4e8 100644 --- a/src/models/routers/__init__.py +++ b/src/models/routers/__init__.py @@ -5,10 +5,6 @@ __all__ = [ - 'CompartmentalModelBase', - 'CompartmentalModel', - 'CModel', - 'AllCModels', 'SimulationType', 'SimulationConfig', ] diff --git a/src/use_cases/cmodels.py b/src/use_cases/cmodels.py index f22045f..2fbe1e8 100644 --- a/src/use_cases/cmodels.py +++ b/src/use_cases/cmodels.py @@ -2,7 +2,6 @@ class CmodelUseCases: @staticmethod def model_information_in_db_to_compare(model_in_db: dict) -> dict: - model_in_db.pop('_id') model_in_db.pop('inserted_at') model_in_db.pop('updated_at') diff --git a/src/utils/cmodels.py b/src/utils/cmodels.py index 3d27c50..7d0a7e2 100644 --- a/src/utils/cmodels.py +++ b/src/utils/cmodels.py @@ -15,8 +15,10 @@ def insert_cmodels_document(): for model in CompartmentalModelEnum: model: CompartmentalModelBase = model.value + id_dict = {'_id': model.name.encode('utf-8').hex()} + cmodel_document = CompartmentalModel( - id=model.name.encode('utf-8').hex(), + id=id_dict['_id'], inserted_at=datetime.now(), updated_at=datetime.now(), **model.dict() @@ -24,7 +26,8 @@ def insert_cmodels_document(): cmodels_crud = MongoCRUD(*get_collection()) - existent_model = cmodels_crud.read({"name": model.name}) + existent_model = cmodels_crud.read(id_dict) + if existent_model: pruned_existent_model = \ CmodelUseCases.model_information_in_db_to_compare( @@ -33,10 +36,16 @@ def insert_cmodels_document(): if pruned_existent_model == model.dict(): # TODO: log cmodel exists ... + print(f'Cmodel exists: {model.name}') else: cmodel_document.pop('inserted_at') - cmodels_crud.update(cmodel_document) + cmodels_crud.update( + id_dict, + cmodel_document + ) # TODO log updated cmodel + print(f'Updated cmodel: {model.name}') else: cmodels_crud.insert(cmodel_document) # TODO: log created cmodel + print(f'Created cmodel: {model.name}') From 76dedd9e7d6168c98fc38aec590c10e9b314fbb3 Mon Sep 17 00:00:00 2001 From: Juan Esteban Aristizabal Date: Wed, 21 Apr 2021 14:46:15 -0500 Subject: [PATCH 16/42] refactor: inserting cmodels in db --- main.py | 4 +-- src/api.py | 3 ++ src/{utils => interfaces}/cmodels.py | 40 +++++++++++++++++--------- src/interfaces/simulation_interface.py | 5 ---- src/models/db/cmodels.py | 12 ++++++-- src/use_cases/cmodels.py | 14 +++++---- 6 files changed, 49 insertions(+), 29 deletions(-) rename src/{utils => interfaces}/cmodels.py (52%) delete mode 100644 src/interfaces/simulation_interface.py diff --git a/main.py b/main.py index 26a408e..678e969 100644 --- a/main.py +++ b/main.py @@ -1,11 +1,9 @@ import uvicorn from src.config import settings -from src.utils.cmodels import insert_cmodels_document -if __name__ == '__main__': - insert_cmodels_document() +if __name__ == '__main__': uvicorn.run( "src.api:app", host=settings['HOST'], diff --git a/src/api.py b/src/api.py index 5585804..133daee 100644 --- a/src/api.py +++ b/src/api.py @@ -4,6 +4,7 @@ from src.config import settings from src.routers.main import main_router +from src.use_cases.cmodels import CmodelUseCases app = FastAPI() @@ -25,3 +26,5 @@ main_router, tags=["Main"], ) + +CmodelUseCases.update_cmodels_collection() diff --git a/src/utils/cmodels.py b/src/interfaces/cmodels.py similarity index 52% rename from src/utils/cmodels.py rename to src/interfaces/cmodels.py index 7d0a7e2..01c6355 100644 --- a/src/utils/cmodels.py +++ b/src/interfaces/cmodels.py @@ -1,19 +1,25 @@ from datetime import datetime -from src.db.mongo import get_collection +from pymongo.collection import Collection +from pymongo.mongo_client import MongoClient + from src.models.db.cmodels import ( CompartmentalModelBase, CompartmentalModelEnum ) from src.models.db.cmodels import CompartmentalModel from src.interfaces.crud import MongoCRUD -from src.use_cases.cmodels import CmodelUseCases -def insert_cmodels_document(): +class CModelsInterface: + def __init__( + self, + db_connection: MongoClient, + collection: Collection + ) -> None: + self.crud = MongoCRUD(db_connection, collection) - for model in CompartmentalModelEnum: - model: CompartmentalModelBase = model.value + def insert_one_cmodel_document(self, model: CompartmentalModelBase): id_dict = {'_id': model.name.encode('utf-8').hex()} @@ -24,28 +30,36 @@ def insert_cmodels_document(): **model.dict() ).dict(by_alias=True) - cmodels_crud = MongoCRUD(*get_collection()) - - existent_model = cmodels_crud.read(id_dict) + existent_model = self.crud.read(id_dict) if existent_model: pruned_existent_model = \ - CmodelUseCases.model_information_in_db_to_compare( + CModelsInterface._prune_db_document( existent_model ) - if pruned_existent_model == model.dict(): + if pruned_existent_model == model.dict(by_alias=True): # TODO: log cmodel exists - ... print(f'Cmodel exists: {model.name}') else: cmodel_document.pop('inserted_at') - cmodels_crud.update( + self.crud.update( id_dict, cmodel_document ) # TODO log updated cmodel print(f'Updated cmodel: {model.name}') else: - cmodels_crud.insert(cmodel_document) + self.crud.insert(cmodel_document) # TODO: log created cmodel print(f'Created cmodel: {model.name}') + + def insert_all_cmodel_documents(self, ): + for model in CompartmentalModelEnum.values(): + self.insert_one_cmodel_document(model) + + @staticmethod + def _prune_db_document(model_in_db: dict) -> dict: + model_in_db.pop('_id') + model_in_db.pop('inserted_at') + model_in_db.pop('updated_at') + return model_in_db diff --git a/src/interfaces/simulation_interface.py b/src/interfaces/simulation_interface.py deleted file mode 100644 index 604a999..0000000 --- a/src/interfaces/simulation_interface.py +++ /dev/null @@ -1,5 +0,0 @@ -from src.db import get_db_connection - - -def simulation_db(): - return get_db_connection().get_collection('cmodels_simulation') diff --git a/src/models/db/cmodels.py b/src/models/db/cmodels.py index 65c3a34..77f667b 100644 --- a/src/models/db/cmodels.py +++ b/src/models/db/cmodels.py @@ -22,7 +22,7 @@ class CompartmentalModelBase(BaseModel): """Units of each parameter. The keys are the 'parameters' array elements""" -class CompartmentalModel(CompartmentalModelBase): +class MetadataBase(BaseModel): id: str = Field(..., alias='_id') inserted_at: datetime updated_at: datetime @@ -31,6 +31,10 @@ class Config: allow_population_by_field_name = True +class CompartmentalModel(MetadataBase, CompartmentalModelBase): + pass + + class CompartmentalModelEnum(Enum): """Compartmental Models' Data. @@ -83,10 +87,14 @@ class CompartmentalModelEnum(Enum): parameters_units={ 'a': 'units of a', 'b': 'units of b', - 'c': 'units_test' + 'c': 'units of c' }, ) + @classmethod + def values(cls) -> List[CompartmentalModelBase]: + return [m.value for m in cls] + class CModel(BaseModel): model: CompartmentalModelEnum diff --git a/src/use_cases/cmodels.py b/src/use_cases/cmodels.py index 2fbe1e8..3db5767 100644 --- a/src/use_cases/cmodels.py +++ b/src/use_cases/cmodels.py @@ -1,8 +1,10 @@ -class CmodelUseCases: +from src.db.mongo import get_collection +from src.interfaces.cmodels import CModelsInterface + +class CmodelUseCases: @staticmethod - def model_information_in_db_to_compare(model_in_db: dict) -> dict: - model_in_db.pop('_id') - model_in_db.pop('inserted_at') - model_in_db.pop('updated_at') - return model_in_db + def update_cmodels_collection(): + db_connection, cmodels_coll = get_collection() + cmodels_interface = CModelsInterface(db_connection, cmodels_coll) + cmodels_interface.insert_all_cmodel_documents() From 547c09cacd002e80d679b36f9b34236a0c7d9f03 Mon Sep 17 00:00:00 2001 From: Alejandro Campillo Date: Thu, 22 Apr 2021 08:13:47 -0500 Subject: [PATCH 17/42] fix: updated model --- src/models/routers/cmodel.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/models/routers/cmodel.py b/src/models/routers/cmodel.py index 8bbc345..5b36360 100644 --- a/src/models/routers/cmodel.py +++ b/src/models/routers/cmodel.py @@ -85,3 +85,6 @@ class AllCModels(BaseModel): models: List[CompartmentalModel] = [ model.value for model in CompartmentalModel ] + + +print(CompartmentalModel(value)) From 79f87f07df5a8d56436b780991ad4d524c818bb4 Mon Sep 17 00:00:00 2001 From: Alejandro Campillo Date: Thu, 22 Apr 2021 08:14:25 -0500 Subject: [PATCH 18/42] fix: cmodels --- src/models/routers/cmodel.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/models/routers/cmodel.py b/src/models/routers/cmodel.py index 5b36360..8bbc345 100644 --- a/src/models/routers/cmodel.py +++ b/src/models/routers/cmodel.py @@ -85,6 +85,3 @@ class AllCModels(BaseModel): models: List[CompartmentalModel] = [ model.value for model in CompartmentalModel ] - - -print(CompartmentalModel(value)) From ec83c827a4dcafc88a449af326657dd19961f506 Mon Sep 17 00:00:00 2001 From: Juan Esteban Aristizabal Date: Thu, 22 Apr 2021 09:43:07 -0500 Subject: [PATCH 19/42] refactor: changed back cmodels documents creation to main.py --- main.py | 2 ++ src/api.py | 4 +--- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/main.py b/main.py index 678e969..43f798d 100644 --- a/main.py +++ b/main.py @@ -1,9 +1,11 @@ import uvicorn from src.config import settings +from src.use_cases.cmodels import CmodelUseCases if __name__ == '__main__': + CmodelUseCases.update_cmodels_collection() uvicorn.run( "src.api:app", host=settings['HOST'], diff --git a/src/api.py b/src/api.py index 133daee..560b554 100644 --- a/src/api.py +++ b/src/api.py @@ -4,7 +4,7 @@ from src.config import settings from src.routers.main import main_router -from src.use_cases.cmodels import CmodelUseCases + app = FastAPI() @@ -26,5 +26,3 @@ main_router, tags=["Main"], ) - -CmodelUseCases.update_cmodels_collection() From aef427e4af14185656130bb27bb22448a720de4e Mon Sep 17 00:00:00 2001 From: Alejandro Campillo Date: Fri, 23 Apr 2021 08:28:44 -0500 Subject: [PATCH 20/42] build: test get_collection method --- tests/db/test_mongo.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/tests/db/test_mongo.py b/tests/db/test_mongo.py index f371b0b..f77c1b0 100644 --- a/tests/db/test_mongo.py +++ b/tests/db/test_mongo.py @@ -3,9 +3,10 @@ from mongomock import patch as db_patch from pymongo.database import Database +from pymongo.collection import Collection from pymongo import MongoClient -from src.db.mongo import api_get_db_connection, get_db +from src.db.mongo import api_get_db_connection, get_db, get_collection def solve_path(path: str): @@ -40,3 +41,12 @@ def test_get_db(self, mock_config: Mock): self.assertIsInstance(db_connection, MongoClient) self.assertIsInstance(db, Database) + + @db_patch(servers=(('mongodb://mongodb.example.com', 27017),)) + @patch(solve_path('db_config')) + def test_get_collection(self, mock_config: Mock): + + db_connection, coll = get_collection() + + self.assertIsInstance(db_connection, MongoClient) + self.assertIsInstance(coll, Collection) From 678cd9b47ff9659d9d01b069674e8e0a7fbf6fd0 Mon Sep 17 00:00:00 2001 From: Alejandro Campillo Date: Fri, 23 Apr 2021 08:57:57 -0500 Subject: [PATCH 21/42] build: test cmodel interface --- tests/interfaces/test_cmodel_interface.py | 39 +++++++++++++++++++ ...ls_interface.py => test_crud_interface.py} | 0 2 files changed, 39 insertions(+) create mode 100644 tests/interfaces/test_cmodel_interface.py rename tests/interfaces/{test_cmodels_interface.py => test_crud_interface.py} (100%) diff --git a/tests/interfaces/test_cmodel_interface.py b/tests/interfaces/test_cmodel_interface.py new file mode 100644 index 0000000..ae73738 --- /dev/null +++ b/tests/interfaces/test_cmodel_interface.py @@ -0,0 +1,39 @@ +from unittest import TestCase +from unittest.mock import patch, Mock + +import mongomock +from mongomock import patch as db_path +from pymongo.results import InsertOneResult + +from src.interfaces.crud import MongoCRUD +from src.interfaces.cmodels import CModelsInterface +from src.models.db.cmodels import ( + CompartmentalModelEnum +) + + +def solve_path(path: str): + source = 'src.db.mongo' + return ".".join([source, path]) + + +class CModelsInterfaceTestCase(TestCase): + server = "mongodb://mongodb0.example.com:27017" + + @db_path(servers=(('server.example.com', 27017),)) + def setUp(self): + self.client = mongomock.MongoClient('server.example.com') + self.test_mock = self.client.db + self.test_collection = self.test_mock.collection + self.mongo_crud = MongoCRUD(self.client, self.test_collection) + + def tearDown(self): + self.client.close() + + @patch(solve_path('get_db')) + def test_insert_one_cmodel_document(self, mock: Mock): + + result = CModelsInterface(self.client, self.test_collection).insert_one_cmodel_document( + CompartmentalModelEnum.values()[1]) + + self.assertIsInstance(result, InsertOneResult) diff --git a/tests/interfaces/test_cmodels_interface.py b/tests/interfaces/test_crud_interface.py similarity index 100% rename from tests/interfaces/test_cmodels_interface.py rename to tests/interfaces/test_crud_interface.py From 0541d800dac5de0ae335e389493e238123e7f1bb Mon Sep 17 00:00:00 2001 From: Alejandro Campillo Date: Fri, 23 Apr 2021 10:08:49 -0500 Subject: [PATCH 22/42] fix: test_cmodel_interface --- tests/interfaces/test_cmodel_interface.py | 33 +++++++++++++++++++---- tests/models/db/test_cmodels.py | 3 +++ 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/tests/interfaces/test_cmodel_interface.py b/tests/interfaces/test_cmodel_interface.py index ae73738..8441c79 100644 --- a/tests/interfaces/test_cmodel_interface.py +++ b/tests/interfaces/test_cmodel_interface.py @@ -3,7 +3,6 @@ import mongomock from mongomock import patch as db_path -from pymongo.results import InsertOneResult from src.interfaces.crud import MongoCRUD from src.interfaces.cmodels import CModelsInterface @@ -31,9 +30,33 @@ def tearDown(self): self.client.close() @patch(solve_path('get_db')) - def test_insert_one_cmodel_document(self, mock: Mock): + def test_insert_one_cmodel_document_ok(self, mock: Mock): - result = CModelsInterface(self.client, self.test_collection).insert_one_cmodel_document( - CompartmentalModelEnum.values()[1]) + self.result = CModelsInterface(self.client, self.test_collection).insert_one_cmodel_document( + CompartmentalModelEnum.values()[0]) - self.assertIsInstance(result, InsertOneResult) + self.assertIsNone(self.result) + + @patch(solve_path('get_db')) + def test_insert_one_cmodel_document_exists(self, mock: Mock): + + CModelsInterface(self.client, self.test_collection).insert_one_cmodel_document( + CompartmentalModelEnum.values()[0]) + + self.result = CModelsInterface(self.client, self.test_collection).insert_one_cmodel_document( + CompartmentalModelEnum.values()[0]) + + self.assertIsNone(self.result) + + @patch(solve_path('get_db')) + def test_insert_one_cmodel_document_update(self, mock: Mock): + + CModelsInterface(self.client, self.test_collection).insert_one_cmodel_document( + CompartmentalModelEnum.values()[0]) + + CompartmentalModelEnum.values()[0].state_variables = ['S', 'I'] + + self.result = CModelsInterface(self.client, self.test_collection).insert_one_cmodel_document( + CompartmentalModelEnum.values()[0]) + + self.assertIsNone(self.result) diff --git a/tests/models/db/test_cmodels.py b/tests/models/db/test_cmodels.py index cfc938e..3603158 100644 --- a/tests/models/db/test_cmodels.py +++ b/tests/models/db/test_cmodels.py @@ -32,3 +32,6 @@ def test_CModel(instance: CModel): def test_AllCmodels(instance: AllCModels): for model in instance.models: assert isinstance(model, CompartmentalModelBase) + + +print(CompartmentalModelEnum.values()[0]) From 1298ea06c0d46f924265226dab7e321e55425922 Mon Sep 17 00:00:00 2001 From: Alejandro Campillo Date: Fri, 23 Apr 2021 10:16:08 -0500 Subject: [PATCH 23/42] build: test delete crud method --- tests/interfaces/test_cmodel_interface.py | 12 ++++++------ tests/interfaces/test_crud_interface.py | 12 +++++++++++- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/tests/interfaces/test_cmodel_interface.py b/tests/interfaces/test_cmodel_interface.py index 8441c79..4536682 100644 --- a/tests/interfaces/test_cmodel_interface.py +++ b/tests/interfaces/test_cmodel_interface.py @@ -32,10 +32,10 @@ def tearDown(self): @patch(solve_path('get_db')) def test_insert_one_cmodel_document_ok(self, mock: Mock): - self.result = CModelsInterface(self.client, self.test_collection).insert_one_cmodel_document( + result = CModelsInterface(self.client, self.test_collection).insert_one_cmodel_document( CompartmentalModelEnum.values()[0]) - self.assertIsNone(self.result) + self.assertIsNone(result) @patch(solve_path('get_db')) def test_insert_one_cmodel_document_exists(self, mock: Mock): @@ -43,10 +43,10 @@ def test_insert_one_cmodel_document_exists(self, mock: Mock): CModelsInterface(self.client, self.test_collection).insert_one_cmodel_document( CompartmentalModelEnum.values()[0]) - self.result = CModelsInterface(self.client, self.test_collection).insert_one_cmodel_document( + result = CModelsInterface(self.client, self.test_collection).insert_one_cmodel_document( CompartmentalModelEnum.values()[0]) - self.assertIsNone(self.result) + self.assertIsNone(result) @patch(solve_path('get_db')) def test_insert_one_cmodel_document_update(self, mock: Mock): @@ -56,7 +56,7 @@ def test_insert_one_cmodel_document_update(self, mock: Mock): CompartmentalModelEnum.values()[0].state_variables = ['S', 'I'] - self.result = CModelsInterface(self.client, self.test_collection).insert_one_cmodel_document( + result = CModelsInterface(self.client, self.test_collection).insert_one_cmodel_document( CompartmentalModelEnum.values()[0]) - self.assertIsNone(self.result) + self.assertIsNone(result) diff --git a/tests/interfaces/test_crud_interface.py b/tests/interfaces/test_crud_interface.py index 5e7d770..d1c00e6 100644 --- a/tests/interfaces/test_crud_interface.py +++ b/tests/interfaces/test_crud_interface.py @@ -3,7 +3,7 @@ import mongomock from mongomock import patch as db_path -from pymongo.results import InsertOneResult +from pymongo.results import InsertOneResult, DeleteResult from src.interfaces.crud import MongoCRUD @@ -69,3 +69,13 @@ def test_update_cmodel_state_fail(self, mock: Mock): result = self.mongo_crud.update(query, {}) self.assertFalse(result) + + @patch(solve_path('get_db')) + def test_delete_cmodel_state_ok(self, mock: Mock): + model = {'_id': 'example'} + + self.mongo_crud.insert(model) + result = self.mongo_crud.delete(model) + + self.assertIsNotNone(result) + self.assertIsInstance(result, DeleteResult) From ce731a843d587542a1ebeeb6bee961249c4db36e Mon Sep 17 00:00:00 2001 From: Juan Esteban Aristizabal Date: Fri, 23 Apr 2021 10:51:15 -0500 Subject: [PATCH 24/42] Update src/interfaces/cmodels.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: David Peña --- src/interfaces/cmodels.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/interfaces/cmodels.py b/src/interfaces/cmodels.py index 01c6355..c64f4a2 100644 --- a/src/interfaces/cmodels.py +++ b/src/interfaces/cmodels.py @@ -25,8 +25,8 @@ def insert_one_cmodel_document(self, model: CompartmentalModelBase): cmodel_document = CompartmentalModel( id=id_dict['_id'], - inserted_at=datetime.now(), - updated_at=datetime.now(), + inserted_at=datetime.utcnow(), + updated_at=datetime.utcnow(), **model.dict() ).dict(by_alias=True) From 51ff8dac3b17e00b29f9dee51036c09351986871 Mon Sep 17 00:00:00 2001 From: Juan Esteban Aristizabal Date: Fri, 23 Apr 2021 10:51:39 -0500 Subject: [PATCH 25/42] Update src/use_cases/cmodels.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: David Peña --- src/use_cases/cmodels.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/use_cases/cmodels.py b/src/use_cases/cmodels.py index 3db5767..4cb9b83 100644 --- a/src/use_cases/cmodels.py +++ b/src/use_cases/cmodels.py @@ -3,6 +3,7 @@ class CmodelUseCases: + @staticmethod def update_cmodels_collection(): db_connection, cmodels_coll = get_collection() From d2e2f698c395448bf7bd2afdaa3a29a9e1e23fcc Mon Sep 17 00:00:00 2001 From: Juan Esteban Aristizabal Date: Fri, 23 Apr 2021 10:53:44 -0500 Subject: [PATCH 26/42] Update main.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: David Peña --- main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.py b/main.py index 43f798d..6eea316 100644 --- a/main.py +++ b/main.py @@ -8,7 +8,7 @@ CmodelUseCases.update_cmodels_collection() uvicorn.run( "src.api:app", - host=settings['HOST'], + host=settings.get('HOST'), port=int(settings['PORT']), reload=True, debug=True, From c615607a445a012d67fad8c415959427239c2e84 Mon Sep 17 00:00:00 2001 From: Juan Esteban Aristizabal Date: Fri, 23 Apr 2021 10:54:19 -0500 Subject: [PATCH 27/42] Update src/interfaces/cmodels.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: David Peña --- src/interfaces/cmodels.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/interfaces/cmodels.py b/src/interfaces/cmodels.py index c64f4a2..3bbbd27 100644 --- a/src/interfaces/cmodels.py +++ b/src/interfaces/cmodels.py @@ -33,10 +33,9 @@ def insert_one_cmodel_document(self, model: CompartmentalModelBase): existent_model = self.crud.read(id_dict) if existent_model: - pruned_existent_model = \ - CModelsInterface._prune_db_document( - existent_model - ) + pruned_existent_model = CModelsInterface._prune_db_document( + existent_model + ) if pruned_existent_model == model.dict(by_alias=True): # TODO: log cmodel exists print(f'Cmodel exists: {model.name}') From f8192ea1f7a7f5b0953121a2c086e5edf087a593 Mon Sep 17 00:00:00 2001 From: Juan Esteban Aristizabal Date: Fri, 23 Apr 2021 10:57:17 -0500 Subject: [PATCH 28/42] Update src/interfaces/cmodels.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: David Peña --- src/interfaces/cmodels.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/interfaces/cmodels.py b/src/interfaces/cmodels.py index 3bbbd27..6734171 100644 --- a/src/interfaces/cmodels.py +++ b/src/interfaces/cmodels.py @@ -52,7 +52,7 @@ def insert_one_cmodel_document(self, model: CompartmentalModelBase): # TODO: log created cmodel print(f'Created cmodel: {model.name}') - def insert_all_cmodel_documents(self, ): + def insert_all_cmodel_documents(self): for model in CompartmentalModelEnum.values(): self.insert_one_cmodel_document(model) From af450a9bfc5a65d069005805d6e09e3fb336bb56 Mon Sep 17 00:00:00 2001 From: Juan Esteban Aristizabal Date: Fri, 23 Apr 2021 10:59:52 -0500 Subject: [PATCH 29/42] fix: style - sort imports --- src/models/db/cmodels.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/models/db/cmodels.py b/src/models/db/cmodels.py index 77f667b..99aaac7 100644 --- a/src/models/db/cmodels.py +++ b/src/models/db/cmodels.py @@ -1,9 +1,9 @@ from datetime import datetime - -from pydantic import BaseModel, Field from enum import Enum from typing import Dict, List +from pydantic import BaseModel, Field + class CompartmentalModelBase(BaseModel): """Base Model for Compartmental Models From 91331f34dcd65250b5307978c083a5b0f5f25879 Mon Sep 17 00:00:00 2001 From: Alejandro Campillo Date: Fri, 23 Apr 2021 12:15:41 -0500 Subject: [PATCH 30/42] fix: interface tests --- src/interfaces/cmodels.py | 11 +++--- tests/interfaces/test_cmodel_interface.py | 42 ++++++++++++++++++----- tests/interfaces/test_crud_interface.py | 8 +++++ tests/models/db/test_cmodels.py | 3 -- 4 files changed, 49 insertions(+), 15 deletions(-) diff --git a/src/interfaces/cmodels.py b/src/interfaces/cmodels.py index 6734171..d3f4cc8 100644 --- a/src/interfaces/cmodels.py +++ b/src/interfaces/cmodels.py @@ -39,22 +39,25 @@ def insert_one_cmodel_document(self, model: CompartmentalModelBase): if pruned_existent_model == model.dict(by_alias=True): # TODO: log cmodel exists print(f'Cmodel exists: {model.name}') + return pruned_existent_model else: cmodel_document.pop('inserted_at') - self.crud.update( + modelupdated = self.crud.update( id_dict, cmodel_document ) # TODO log updated cmodel print(f'Updated cmodel: {model.name}') + return modelupdated else: - self.crud.insert(cmodel_document) + model_inserted = self.crud.insert(cmodel_document) # TODO: log created cmodel print(f'Created cmodel: {model.name}') + return model_inserted def insert_all_cmodel_documents(self): - for model in CompartmentalModelEnum.values(): - self.insert_one_cmodel_document(model) + + return [self.insert_one_cmodel_document(model) for model in CompartmentalModelEnum.values()] @staticmethod def _prune_db_document(model_in_db: dict) -> dict: diff --git a/tests/interfaces/test_cmodel_interface.py b/tests/interfaces/test_cmodel_interface.py index 4536682..9e582ef 100644 --- a/tests/interfaces/test_cmodel_interface.py +++ b/tests/interfaces/test_cmodel_interface.py @@ -3,6 +3,7 @@ import mongomock from mongomock import patch as db_path +from pymongo.results import InsertOneResult from src.interfaces.crud import MongoCRUD from src.interfaces.cmodels import CModelsInterface @@ -25,6 +26,8 @@ def setUp(self): self.test_mock = self.client.db self.test_collection = self.test_mock.collection self.mongo_crud = MongoCRUD(self.client, self.test_collection) + self.mock_interface = CModelsInterface( + self.client, self.test_collection) def tearDown(self): self.client.close() @@ -32,31 +35,54 @@ def tearDown(self): @patch(solve_path('get_db')) def test_insert_one_cmodel_document_ok(self, mock: Mock): - result = CModelsInterface(self.client, self.test_collection).insert_one_cmodel_document( + result = self.mock_interface.insert_one_cmodel_document( CompartmentalModelEnum.values()[0]) - self.assertIsNone(result) + self.assertIsNotNone(result) + self.assertIsInstance(result, InsertOneResult) @patch(solve_path('get_db')) def test_insert_one_cmodel_document_exists(self, mock: Mock): - CModelsInterface(self.client, self.test_collection).insert_one_cmodel_document( + self.mock_interface.insert_one_cmodel_document( CompartmentalModelEnum.values()[0]) - result = CModelsInterface(self.client, self.test_collection).insert_one_cmodel_document( + result = self.mock_interface.insert_one_cmodel_document( CompartmentalModelEnum.values()[0]) - self.assertIsNone(result) + self.assertIsNotNone(result) @patch(solve_path('get_db')) def test_insert_one_cmodel_document_update(self, mock: Mock): - CModelsInterface(self.client, self.test_collection).insert_one_cmodel_document( + self.mock_interface.insert_one_cmodel_document( CompartmentalModelEnum.values()[0]) CompartmentalModelEnum.values()[0].state_variables = ['S', 'I'] - result = CModelsInterface(self.client, self.test_collection).insert_one_cmodel_document( + result = self.mock_interface.insert_one_cmodel_document( CompartmentalModelEnum.values()[0]) - self.assertIsNone(result) + self.assertIsNotNone(result) + self.assertTrue(result) + + @patch(solve_path('get_db')) + def test_prune_db_document(self, mock: Mock): + + query = {'name': 'SIR'} + + self.mock_interface.insert_one_cmodel_document( + CompartmentalModelEnum.values()[0]) + + read_model = self.mock_interface.crud.read(query) + result = self.mock_interface._prune_db_document(read_model) + + self.assertIsNotNone(result) + + @patch(solve_path('get_db')) + def test_insert_all_models(self, mock: Mock): + + result = self.mock_interface.insert_all_cmodel_documents() + + self.assertIsNotNone(result) + self.assertIsInstance(result[0], InsertOneResult) diff --git a/tests/interfaces/test_crud_interface.py b/tests/interfaces/test_crud_interface.py index d1c00e6..ae57680 100644 --- a/tests/interfaces/test_crud_interface.py +++ b/tests/interfaces/test_crud_interface.py @@ -70,6 +70,14 @@ def test_update_cmodel_state_fail(self, mock: Mock): self.assertFalse(result) + @patch(solve_path('get_db')) + def test_update_cmodel_state_no_query(self, mock: Mock): + query = {} + + result = self.mongo_crud.update(query, {}) + + self.assertFalse(result) + @patch(solve_path('get_db')) def test_delete_cmodel_state_ok(self, mock: Mock): model = {'_id': 'example'} diff --git a/tests/models/db/test_cmodels.py b/tests/models/db/test_cmodels.py index 3603158..cfc938e 100644 --- a/tests/models/db/test_cmodels.py +++ b/tests/models/db/test_cmodels.py @@ -32,6 +32,3 @@ def test_CModel(instance: CModel): def test_AllCmodels(instance: AllCModels): for model in instance.models: assert isinstance(model, CompartmentalModelBase) - - -print(CompartmentalModelEnum.values()[0]) From 451a2f5dc92af3737aea7bd2a31e00dd3e8bb0f3 Mon Sep 17 00:00:00 2001 From: Juan Esteban Aristizabal Date: Fri, 23 Apr 2021 15:56:42 -0500 Subject: [PATCH 31/42] refactor: interfaces and appropiate tests --- src/interfaces/cmodels.py | 53 ++++++-------- src/interfaces/crud.py | 39 +++++++--- src/models/db/base_model.py | 26 +++++++ src/models/db/cmodels.py | 27 +++---- .../simulation.py => db/simulations.py} | 14 +--- tests/interfaces/test_cmodel_interface.py | 66 +++++++++++++---- tests/interfaces/test_crud_interface.py | 71 +++++++++++-------- tests/models/db/test_base_model.py | 13 ++++ .../test_simulations.py} | 10 ++- 9 files changed, 206 insertions(+), 113 deletions(-) create mode 100644 src/models/db/base_model.py rename src/models/{routers/simulation.py => db/simulations.py} (53%) create mode 100644 tests/models/db/test_base_model.py rename tests/models/{routers/test_simulation.py => db/test_simulations.py} (69%) diff --git a/src/interfaces/cmodels.py b/src/interfaces/cmodels.py index d3f4cc8..3e6c002 100644 --- a/src/interfaces/cmodels.py +++ b/src/interfaces/cmodels.py @@ -4,14 +4,14 @@ from pymongo.mongo_client import MongoClient from src.models.db.cmodels import ( - CompartmentalModelBase, + CompartmentalModel, CompartmentalModelEnum ) -from src.models.db.cmodels import CompartmentalModel from src.interfaces.crud import MongoCRUD class CModelsInterface: + def __init__( self, db_connection: MongoClient, @@ -19,49 +19,42 @@ def __init__( ) -> None: self.crud = MongoCRUD(db_connection, collection) - def insert_one_cmodel_document(self, model: CompartmentalModelBase): - - id_dict = {'_id': model.name.encode('utf-8').hex()} + def insert_one_cmodel_document(self, model: CompartmentalModel): - cmodel_document = CompartmentalModel( - id=id_dict['_id'], - inserted_at=datetime.utcnow(), - updated_at=datetime.utcnow(), - **model.dict() - ).dict(by_alias=True) + cmodel_document = model.dict(by_alias=True) - existent_model = self.crud.read(id_dict) + existent_model = self.crud.read(model.id) if existent_model: pruned_existent_model = CModelsInterface._prune_db_document( existent_model ) - if pruned_existent_model == model.dict(by_alias=True): - # TODO: log cmodel exists - print(f'Cmodel exists: {model.name}') - return pruned_existent_model + pruned_current_model = CModelsInterface._prune_db_document( + cmodel_document + ) + if pruned_existent_model == pruned_current_model: + # TODO: log cmodel exists f'Cmodel exists: {model.name}' + return existent_model else: - cmodel_document.pop('inserted_at') - modelupdated = self.crud.update( - id_dict, - cmodel_document + model.updated_at = datetime.utcnow() + updated_model = self.crud.update( + model.id, + model.dict(by_alias=True) ) - # TODO log updated cmodel - print(f'Updated cmodel: {model.name}') - return modelupdated + # TODO log updated cmodel f'Updated cmodel: {model.name}' + return updated_model else: model_inserted = self.crud.insert(cmodel_document) - # TODO: log created cmodel - print(f'Created cmodel: {model.name}') + # TODO: log created cmodel f'Created cmodel: {model.name}' return model_inserted def insert_all_cmodel_documents(self): - return [self.insert_one_cmodel_document(model) for model in CompartmentalModelEnum.values()] @staticmethod def _prune_db_document(model_in_db: dict) -> dict: - model_in_db.pop('_id') - model_in_db.pop('inserted_at') - model_in_db.pop('updated_at') - return model_in_db + if model_in_db: + model_in_db.pop('_id') + model_in_db.pop('inserted_at') + model_in_db.pop('updated_at') + return model_in_db diff --git a/src/interfaces/crud.py b/src/interfaces/crud.py index 94ce9df..7026821 100644 --- a/src/interfaces/crud.py +++ b/src/interfaces/crud.py @@ -1,4 +1,5 @@ from typing import Any, Union +from bson.objectid import ObjectId from pymongo.collection import Collection from pymongo.mongo_client import MongoClient @@ -25,9 +26,26 @@ def insert(self, document: dict): model: pymongo object """ with self.db_connection: + try: + document['_id'] + except KeyError: + raise ValueError('self.insert(): document must have key "_id"') + + existent_document = self.collection.find_one( + self._id_to_dict(document['_id']) + ) + + if existent_document: + raise ValueError( + f'Document with _id={document["_id"]} already exists in' + 'collection {self.collection}. if you want to change the' + 'fields\' values please use self.update' + ) + # TODO: log + return self.collection.insert_one(document) - def read(self, query: dict) -> Union[Any, None]: + def read(self, _id: ObjectId) -> Union[Any, None]: """Search for a specific model in ``self.collection``. Parameters @@ -41,9 +59,9 @@ def read(self, query: dict) -> Union[Any, None]: Object containing the results of the search """ with self.db_connection: - return self.collection.find_one(query) + return self.collection.find_one(self._id_to_dict(_id)) - def update(self, query: dict, new_data: dict) -> bool: + def update(self, _id: ObjectId, new_data: dict) -> bool: """Update document in ``self.collection``. Parameters @@ -62,20 +80,23 @@ def update(self, query: dict, new_data: dict) -> bool: True: If the model has valid data and its status can be updated """ - + _id_dict = self._id_to_dict(_id) with self.db_connection: - if not query: + if not _id_dict: return False - cmodel = self.collection.find_one(query) + cmodel = self.collection.find_one(_id_dict) if cmodel: update_model = self.collection.update_one( - query, + _id_dict, {"$set": new_data}, ) if update_model: return True return False - def delete(self, query): + def delete(self, _id: ObjectId): with self.db_connection: - return self.collection.delete_one(query) + return self.collection.delete_one(self._id_to_dict(_id)) + + def _id_to_dict(self, _id: ObjectId): + return {'_id': _id} if _id else None diff --git a/src/models/db/base_model.py b/src/models/db/base_model.py new file mode 100644 index 0000000..202c3f6 --- /dev/null +++ b/src/models/db/base_model.py @@ -0,0 +1,26 @@ +from datetime import datetime + +from pydantic import BaseModel, Field +from bson.objectid import ObjectId as BsonObjectId + + +class PydanticObjectId(BsonObjectId): + @classmethod + def __get_validators__(cls): + yield cls.validate + + @classmethod + def validate(cls, v): + if not isinstance(v, BsonObjectId): + raise TypeError('ObjectId required') + return v + + +class MetadataBaseDoc(BaseModel): + id: PydanticObjectId = Field(..., alias='_id') + inserted_at: datetime = datetime.utcnow() + updated_at: datetime = datetime.utcnow() + + class Config: + allow_population_by_field_name = True + extra = 'forbid' diff --git a/src/models/db/cmodels.py b/src/models/db/cmodels.py index 99aaac7..8a6b17f 100644 --- a/src/models/db/cmodels.py +++ b/src/models/db/cmodels.py @@ -1,9 +1,10 @@ -from datetime import datetime from enum import Enum from typing import Dict, List +from bson.objectid import ObjectId -from pydantic import BaseModel, Field +from pydantic import BaseModel +from .base_model import MetadataBaseDoc class CompartmentalModelBase(BaseModel): """Base Model for Compartmental Models @@ -22,16 +23,7 @@ class CompartmentalModelBase(BaseModel): """Units of each parameter. The keys are the 'parameters' array elements""" -class MetadataBase(BaseModel): - id: str = Field(..., alias='_id') - inserted_at: datetime - updated_at: datetime - - class Config: - allow_population_by_field_name = True - - -class CompartmentalModel(MetadataBase, CompartmentalModelBase): +class CompartmentalModel(MetadataBaseDoc, CompartmentalModelBase): pass @@ -42,7 +34,8 @@ class CompartmentalModelEnum(Enum): on the corresponding Compartmental Model. Each element is a :class:``CompartmentalModelBase`` object """ - sir: CompartmentalModelBase = CompartmentalModelBase( + sir: CompartmentalModelBase = CompartmentalModel( + id=ObjectId('6083175ea91f5aacea234423'), name='SIR', state_variables=['S', 'I', 'R'], state_variables_units={ @@ -57,7 +50,8 @@ class CompartmentalModelEnum(Enum): }, ) - seir: CompartmentalModelBase = CompartmentalModelBase( + seir: CompartmentalModelBase = CompartmentalModel( + id=ObjectId('6083176ca91f5aacea234424'), name='SEIR', state_variables=['S', 'E', 'I', 'R'], state_variables_units={ @@ -73,7 +67,8 @@ class CompartmentalModelEnum(Enum): }, ) - seirv: CompartmentalModelBase = CompartmentalModelBase( + seirv: CompartmentalModelBase = CompartmentalModel( + id=ObjectId('608317d0a91f5aacea234426'), name='SEIRV', state_variables=['S', 'E', 'I', 'R', 'V'], state_variables_units={ @@ -92,7 +87,7 @@ class CompartmentalModelEnum(Enum): ) @classmethod - def values(cls) -> List[CompartmentalModelBase]: + def values(cls) -> List[CompartmentalModel]: return [m.value for m in cls] diff --git a/src/models/routers/simulation.py b/src/models/db/simulations.py similarity index 53% rename from src/models/routers/simulation.py rename to src/models/db/simulations.py index 2d87447..39321fa 100644 --- a/src/models/routers/simulation.py +++ b/src/models/db/simulations.py @@ -1,20 +1,8 @@ from enum import Enum from datetime import datetime -from bson.objectid import ObjectId as BsonObjectId from pydantic import BaseModel - - -class PydanticObjectId(BsonObjectId): - @classmethod - def __get_validators__(cls): - yield cls.validate - - @classmethod - def validate(cls, v): - if not isinstance(v, BsonObjectId): - raise TypeError('ObjectId required') - return v +from.base_model import PydanticObjectId class SimulationType(str, Enum): diff --git a/tests/interfaces/test_cmodel_interface.py b/tests/interfaces/test_cmodel_interface.py index 9e582ef..6fafb09 100644 --- a/tests/interfaces/test_cmodel_interface.py +++ b/tests/interfaces/test_cmodel_interface.py @@ -27,7 +27,9 @@ def setUp(self): self.test_collection = self.test_mock.collection self.mongo_crud = MongoCRUD(self.client, self.test_collection) self.mock_interface = CModelsInterface( - self.client, self.test_collection) + self.client, self.test_collection + ) + self.cmodel_example_document = CompartmentalModelEnum.sir.value def tearDown(self): self.client.close() @@ -36,7 +38,8 @@ def tearDown(self): def test_insert_one_cmodel_document_ok(self, mock: Mock): result = self.mock_interface.insert_one_cmodel_document( - CompartmentalModelEnum.values()[0]) + self.cmodel_example_document + ) self.assertIsNotNone(result) self.assertIsInstance(result, InsertOneResult) @@ -45,23 +48,34 @@ def test_insert_one_cmodel_document_ok(self, mock: Mock): def test_insert_one_cmodel_document_exists(self, mock: Mock): self.mock_interface.insert_one_cmodel_document( - CompartmentalModelEnum.values()[0]) + self.cmodel_example_document + ) result = self.mock_interface.insert_one_cmodel_document( - CompartmentalModelEnum.values()[0]) + self.cmodel_example_document + ) - self.assertIsNotNone(result) + pruned_example_document = CModelsInterface._prune_db_document( + self.cmodel_example_document.dict(by_alias=True) + ) + + self.assertEqual( + result, + pruned_example_document + ) @patch(solve_path('get_db')) def test_insert_one_cmodel_document_update(self, mock: Mock): self.mock_interface.insert_one_cmodel_document( - CompartmentalModelEnum.values()[0]) + self.cmodel_example_document + ) - CompartmentalModelEnum.values()[0].state_variables = ['S', 'I'] + self.cmodel_example_document.state_variables = ['S', 'I'] result = self.mock_interface.insert_one_cmodel_document( - CompartmentalModelEnum.values()[0]) + self.cmodel_example_document + ) self.assertIsNotNone(result) self.assertTrue(result) @@ -69,15 +83,37 @@ def test_insert_one_cmodel_document_update(self, mock: Mock): @patch(solve_path('get_db')) def test_prune_db_document(self, mock: Mock): - query = {'name': 'SIR'} + _id = self.cmodel_example_document.id self.mock_interface.insert_one_cmodel_document( - CompartmentalModelEnum.values()[0]) - - read_model = self.mock_interface.crud.read(query) - result = self.mock_interface._prune_db_document(read_model) - - self.assertIsNotNone(result) + self.cmodel_example_document + ) + + read_model = self.mock_interface.crud.read(_id) + pruned_document = self.mock_interface._prune_db_document(read_model) + + self.assertIsNotNone(pruned_document) + + try: + pruned_document['_id'] + except KeyError: + self.assertTrue(True) + else: + self.fail('_id key not expected') + + try: + pruned_document['inserted_at'] + except KeyError: + self.assertTrue(True) + else: + self.fail('inserted_at key not expected') + + try: + pruned_document['updated_at'] + except KeyError: + self.assertTrue(True) + else: + self.fail('updated_at key not expected') @patch(solve_path('get_db')) def test_insert_all_models(self, mock: Mock): diff --git a/tests/interfaces/test_crud_interface.py b/tests/interfaces/test_crud_interface.py index ae57680..b167261 100644 --- a/tests/interfaces/test_crud_interface.py +++ b/tests/interfaces/test_crud_interface.py @@ -6,6 +6,7 @@ from pymongo.results import InsertOneResult, DeleteResult from src.interfaces.crud import MongoCRUD +from src.models.db.cmodels import CompartmentalModelEnum def solve_path(path: str): @@ -22,68 +23,80 @@ def setUp(self): self.test_mock = self.client.db self.test_collection = self.test_mock.collection self.mongo_crud = MongoCRUD(self.client, self.test_collection) + self._id_example = CompartmentalModelEnum.sir.value.id + self.model_example = {'_id': self._id_example} def tearDown(self): self.client.close() @patch(solve_path('get_db')) - def test_insert_cmodel_ok(self, mock: Mock): - model = {"_id": "example"} - - result = self.mongo_crud.insert(model) + def test_insert_ok(self, mock: Mock): + result = self.mongo_crud.insert(self.model_example) self.assertIsNotNone(result) self.assertIsInstance(result, InsertOneResult) @patch(solve_path('get_db')) - def test_read_cmodel_ok(self, mock: Mock): - model = {"_id": "example"} + def test_insert_no_id_present_in_document(self, mock: Mock): + try: + self.mongo_crud.insert( + {'no_id_present': 'not_a_valid_query'} + ) + except ValueError: + self.assertTrue(True) + else: + self.fail('_id must be present in inserted document') - self.mongo_crud.insert(model) - read_result = self.mongo_crud.read(model) + @patch(solve_path('get_db')) + def test_insert_existent_document(self, mock: Mock): + self.mongo_crud.insert(self.model_example) + try: + self.mongo_crud.insert(self.model_example) + except ValueError: + self.assertTrue(True) + else: + self.fail('_id must be present in inserted document') + + @patch(solve_path('get_db')) + def test_read_ok(self, mock: Mock): + self.mongo_crud.insert(self.model_example) + read_result = self.mongo_crud.read(self._id_example) self.assertIsNotNone(read_result) self.assertIsInstance(read_result, dict) @patch(solve_path('get_db')) - def test_read_cmodel_not_found(self, mock: Mock): - model = {"_id": "example"} - result = self.mongo_crud.read(model) + def test_read_not_found(self, mock: Mock): + result = self.mongo_crud.read(self._id_example) self.assertIsNone(result) @patch(solve_path('get_db')) def test_update_cmodel_state_ok(self, mock: Mock): - model = {"_id": "example"} - - self.mongo_crud.insert(model) + self.mongo_crud.insert(self.model_example) new_data = {'params': ['a', 'b', 'c']} - result = self.mongo_crud.update(model, new_data) + result = self.mongo_crud.update( + self._id_example, + new_data + ) self.assertTrue(result) @patch(solve_path('get_db')) - def test_update_cmodel_state_fail(self, mock: Mock): - query = {'_id': 'test_example'} - - result = self.mongo_crud.update(query, {}) + def test_update_state_fail(self, mock: Mock): + result = self.mongo_crud.update(self._id_example, {}) self.assertFalse(result) @patch(solve_path('get_db')) - def test_update_cmodel_state_no_query(self, mock: Mock): - query = {} - - result = self.mongo_crud.update(query, {}) - + def test_update_state_no_query(self, mock: Mock): + result = self.mongo_crud.update(None, {}) self.assertFalse(result) @patch(solve_path('get_db')) - def test_delete_cmodel_state_ok(self, mock: Mock): - model = {'_id': 'example'} - - self.mongo_crud.insert(model) - result = self.mongo_crud.delete(model) + def test_delete_state_ok(self, mock: Mock): + self.mongo_crud.insert(self.model_example) + result = self.mongo_crud.delete(self._id_example) self.assertIsNotNone(result) self.assertIsInstance(result, DeleteResult) diff --git a/tests/models/db/test_base_model.py b/tests/models/db/test_base_model.py new file mode 100644 index 0000000..9af1743 --- /dev/null +++ b/tests/models/db/test_base_model.py @@ -0,0 +1,13 @@ +from pydantic import ValidationError +import pytest + +from src.models.db.base_model import MetadataBaseDoc + + +def test_SimulationConfig(): + try: + MetadataBaseDoc(id='not a bson.ObjectID') + except ValidationError: + assert True + else: + assert pytest.fail('MetadataBaseDoc must contain id field') diff --git a/tests/models/routers/test_simulation.py b/tests/models/db/test_simulations.py similarity index 69% rename from tests/models/routers/test_simulation.py rename to tests/models/db/test_simulations.py index 3b2fe0b..77e3577 100644 --- a/tests/models/routers/test_simulation.py +++ b/tests/models/db/test_simulations.py @@ -1,8 +1,10 @@ from datetime import datetime +import pytest +from pydantic import ValidationError from hypothesis import given, strategies as st -from src.models.routers.simulation import ( +from src.models.db.simulations import ( SimulationType, SimulationConfig, PydanticObjectId @@ -22,3 +24,9 @@ def test_SimulationConfig(instance: SimulationConfig): assert isinstance(instance.name, str) assert isinstance(instance.creation_date, datetime) assert isinstance(instance.simulation_type, SimulationType) + + +@given(st.builds(SimulationConfig)) +def test_SimulationConfig_bad_request(instance: SimulationConfig): + with pytest.raises(ValidationError): + SimulationConfig(**{'bad_field_name': 'random value'}) From 65edffcb978297fef2f393d7156a1ce76d7708f2 Mon Sep 17 00:00:00 2001 From: Juan Esteban Aristizabal Date: Fri, 23 Apr 2021 17:37:35 -0500 Subject: [PATCH 32/42] tests: cmodel use_cases --- src/interfaces/cmodels.py | 1 - src/models/routers/__init__.py | 10 ------ src/use_cases/cmodels.py | 8 +++-- tests/interfaces/test_cmodel_interface.py | 7 ---- tests/use_cases/__init__.py | 0 tests/use_cases/test_cmodels.py | 39 +++++++++++++++++++++++ 6 files changed, 44 insertions(+), 21 deletions(-) create mode 100644 tests/use_cases/__init__.py create mode 100644 tests/use_cases/test_cmodels.py diff --git a/src/interfaces/cmodels.py b/src/interfaces/cmodels.py index 3e6c002..976e992 100644 --- a/src/interfaces/cmodels.py +++ b/src/interfaces/cmodels.py @@ -54,7 +54,6 @@ def insert_all_cmodel_documents(self): @staticmethod def _prune_db_document(model_in_db: dict) -> dict: if model_in_db: - model_in_db.pop('_id') model_in_db.pop('inserted_at') model_in_db.pop('updated_at') return model_in_db diff --git a/src/models/routers/__init__.py b/src/models/routers/__init__.py index f56f4e8..e69de29 100644 --- a/src/models/routers/__init__.py +++ b/src/models/routers/__init__.py @@ -1,10 +0,0 @@ -from .simulation import ( - SimulationType, - SimulationConfig -) - - -__all__ = [ - 'SimulationType', - 'SimulationConfig', -] diff --git a/src/use_cases/cmodels.py b/src/use_cases/cmodels.py index 4cb9b83..1c6c1a8 100644 --- a/src/use_cases/cmodels.py +++ b/src/use_cases/cmodels.py @@ -3,9 +3,11 @@ class CmodelUseCases: + db_connection, cmodels_coll = get_collection() + cmodels_interface = CModelsInterface(db_connection, cmodels_coll) @staticmethod - def update_cmodels_collection(): - db_connection, cmodels_coll = get_collection() - cmodels_interface = CModelsInterface(db_connection, cmodels_coll) + def update_cmodels_collection( + cmodels_interface: CModelsInterface = cmodels_interface + ): cmodels_interface.insert_all_cmodel_documents() diff --git a/tests/interfaces/test_cmodel_interface.py b/tests/interfaces/test_cmodel_interface.py index 6fafb09..aa5534b 100644 --- a/tests/interfaces/test_cmodel_interface.py +++ b/tests/interfaces/test_cmodel_interface.py @@ -94,13 +94,6 @@ def test_prune_db_document(self, mock: Mock): self.assertIsNotNone(pruned_document) - try: - pruned_document['_id'] - except KeyError: - self.assertTrue(True) - else: - self.fail('_id key not expected') - try: pruned_document['inserted_at'] except KeyError: diff --git a/tests/use_cases/__init__.py b/tests/use_cases/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/use_cases/test_cmodels.py b/tests/use_cases/test_cmodels.py new file mode 100644 index 0000000..ace589a --- /dev/null +++ b/tests/use_cases/test_cmodels.py @@ -0,0 +1,39 @@ + +from unittest import TestCase +from unittest.mock import patch, Mock + +import mongomock +from mongomock import patch as db_path + +from src.use_cases.cmodels import CmodelUseCases +from src.interfaces.crud import MongoCRUD +from src.interfaces.cmodels import CModelsInterface +from src.models.db.cmodels import CompartmentalModelEnum + + +class CmodelUseCasesTestCase(TestCase): + server = "mongodb://mongodb0.example.com:27017" + + @db_path(servers=(('server.example.com', 27017),)) + def setUp(self): + self.connection_mock = mongomock.MongoClient('server.example.com') + self.db_mock = self.connection_mock.db + self.collection_mock = self.db_mock.collection + self.mongo_crud_mock = MongoCRUD( + self.connection_mock, self.collection_mock + ) + self.cmodels_interface_mock = CModelsInterface( + self.connection_mock, self.collection_mock + ) + CmodelUseCases.update_cmodels_collection( + self.cmodels_interface_mock + ) + + def tearDown(self): + self.connection_mock.close() + + @patch('src.db.mongo.get_db') + def test_cmodels_in_ok(self, mock: Mock): + for model in CompartmentalModelEnum.values(): + result = self.mongo_crud_mock.read(model.id) + self.assertIsNotNone(result) From 3a91fa2c6032d4b7aabbcd659614d200759ff317 Mon Sep 17 00:00:00 2001 From: Juan Esteban Aristizabal Date: Fri, 23 Apr 2021 17:47:27 -0500 Subject: [PATCH 33/42] refactor: renamed attributes in src.interfaces test cases --- tests/interfaces/test_cmodel_interface.py | 32 ++++++++++---------- tests/interfaces/test_crud_interface.py | 36 +++++++++++------------ 2 files changed, 33 insertions(+), 35 deletions(-) diff --git a/tests/interfaces/test_cmodel_interface.py b/tests/interfaces/test_cmodel_interface.py index aa5534b..3d6c36a 100644 --- a/tests/interfaces/test_cmodel_interface.py +++ b/tests/interfaces/test_cmodel_interface.py @@ -5,7 +5,6 @@ from mongomock import patch as db_path from pymongo.results import InsertOneResult -from src.interfaces.crud import MongoCRUD from src.interfaces.cmodels import CModelsInterface from src.models.db.cmodels import ( CompartmentalModelEnum @@ -22,22 +21,21 @@ class CModelsInterfaceTestCase(TestCase): @db_path(servers=(('server.example.com', 27017),)) def setUp(self): - self.client = mongomock.MongoClient('server.example.com') - self.test_mock = self.client.db - self.test_collection = self.test_mock.collection - self.mongo_crud = MongoCRUD(self.client, self.test_collection) - self.mock_interface = CModelsInterface( - self.client, self.test_collection + self.connection_mock = mongomock.MongoClient('server.example.com') + self.db_mock = self.connection_mock.db + self.collection_mock = self.db_mock.collection + self.cmodels_interface_mock = CModelsInterface( + self.connection_mock, self.collection_mock ) self.cmodel_example_document = CompartmentalModelEnum.sir.value def tearDown(self): - self.client.close() + self.connection_mock.close() @patch(solve_path('get_db')) def test_insert_one_cmodel_document_ok(self, mock: Mock): - result = self.mock_interface.insert_one_cmodel_document( + result = self.cmodels_interface_mock.insert_one_cmodel_document( self.cmodel_example_document ) @@ -47,11 +45,11 @@ def test_insert_one_cmodel_document_ok(self, mock: Mock): @patch(solve_path('get_db')) def test_insert_one_cmodel_document_exists(self, mock: Mock): - self.mock_interface.insert_one_cmodel_document( + self.cmodels_interface_mock.insert_one_cmodel_document( self.cmodel_example_document ) - result = self.mock_interface.insert_one_cmodel_document( + result = self.cmodels_interface_mock.insert_one_cmodel_document( self.cmodel_example_document ) @@ -67,13 +65,13 @@ def test_insert_one_cmodel_document_exists(self, mock: Mock): @patch(solve_path('get_db')) def test_insert_one_cmodel_document_update(self, mock: Mock): - self.mock_interface.insert_one_cmodel_document( + self.cmodels_interface_mock.insert_one_cmodel_document( self.cmodel_example_document ) self.cmodel_example_document.state_variables = ['S', 'I'] - result = self.mock_interface.insert_one_cmodel_document( + result = self.cmodels_interface_mock.insert_one_cmodel_document( self.cmodel_example_document ) @@ -85,12 +83,12 @@ def test_prune_db_document(self, mock: Mock): _id = self.cmodel_example_document.id - self.mock_interface.insert_one_cmodel_document( + self.cmodels_interface_mock.insert_one_cmodel_document( self.cmodel_example_document ) - read_model = self.mock_interface.crud.read(_id) - pruned_document = self.mock_interface._prune_db_document(read_model) + read_model = self.cmodels_interface_mock.crud.read(_id) + pruned_document = self.cmodels_interface_mock._prune_db_document(read_model) self.assertIsNotNone(pruned_document) @@ -111,7 +109,7 @@ def test_prune_db_document(self, mock: Mock): @patch(solve_path('get_db')) def test_insert_all_models(self, mock: Mock): - result = self.mock_interface.insert_all_cmodel_documents() + result = self.cmodels_interface_mock.insert_all_cmodel_documents() self.assertIsNotNone(result) self.assertIsInstance(result[0], InsertOneResult) diff --git a/tests/interfaces/test_crud_interface.py b/tests/interfaces/test_crud_interface.py index b167261..096b9d8 100644 --- a/tests/interfaces/test_crud_interface.py +++ b/tests/interfaces/test_crud_interface.py @@ -19,19 +19,19 @@ class MongoCRUDTestCase(TestCase): @db_path(servers=(('server.example.com', 27017),)) def setUp(self): - self.client = mongomock.MongoClient('server.example.com') - self.test_mock = self.client.db - self.test_collection = self.test_mock.collection - self.mongo_crud = MongoCRUD(self.client, self.test_collection) + self.connection_mock = mongomock.MongoClient('server.example.com') + self.db_mock = self.connection_mock.db + self.collection_mock = self.db_mock.collection + self.mongo_crud_mock = MongoCRUD(self.connection_mock, self.collection_mock) self._id_example = CompartmentalModelEnum.sir.value.id self.model_example = {'_id': self._id_example} def tearDown(self): - self.client.close() + self.connection_mock.close() @patch(solve_path('get_db')) def test_insert_ok(self, mock: Mock): - result = self.mongo_crud.insert(self.model_example) + result = self.mongo_crud_mock.insert(self.model_example) self.assertIsNotNone(result) self.assertIsInstance(result, InsertOneResult) @@ -39,7 +39,7 @@ def test_insert_ok(self, mock: Mock): @patch(solve_path('get_db')) def test_insert_no_id_present_in_document(self, mock: Mock): try: - self.mongo_crud.insert( + self.mongo_crud_mock.insert( {'no_id_present': 'not_a_valid_query'} ) except ValueError: @@ -49,9 +49,9 @@ def test_insert_no_id_present_in_document(self, mock: Mock): @patch(solve_path('get_db')) def test_insert_existent_document(self, mock: Mock): - self.mongo_crud.insert(self.model_example) + self.mongo_crud_mock.insert(self.model_example) try: - self.mongo_crud.insert(self.model_example) + self.mongo_crud_mock.insert(self.model_example) except ValueError: self.assertTrue(True) else: @@ -59,23 +59,23 @@ def test_insert_existent_document(self, mock: Mock): @patch(solve_path('get_db')) def test_read_ok(self, mock: Mock): - self.mongo_crud.insert(self.model_example) - read_result = self.mongo_crud.read(self._id_example) + self.mongo_crud_mock.insert(self.model_example) + read_result = self.mongo_crud_mock.read(self._id_example) self.assertIsNotNone(read_result) self.assertIsInstance(read_result, dict) @patch(solve_path('get_db')) def test_read_not_found(self, mock: Mock): - result = self.mongo_crud.read(self._id_example) + result = self.mongo_crud_mock.read(self._id_example) self.assertIsNone(result) @patch(solve_path('get_db')) def test_update_cmodel_state_ok(self, mock: Mock): - self.mongo_crud.insert(self.model_example) + self.mongo_crud_mock.insert(self.model_example) new_data = {'params': ['a', 'b', 'c']} - result = self.mongo_crud.update( + result = self.mongo_crud_mock.update( self._id_example, new_data ) @@ -84,19 +84,19 @@ def test_update_cmodel_state_ok(self, mock: Mock): @patch(solve_path('get_db')) def test_update_state_fail(self, mock: Mock): - result = self.mongo_crud.update(self._id_example, {}) + result = self.mongo_crud_mock.update(self._id_example, {}) self.assertFalse(result) @patch(solve_path('get_db')) def test_update_state_no_query(self, mock: Mock): - result = self.mongo_crud.update(None, {}) + result = self.mongo_crud_mock.update(None, {}) self.assertFalse(result) @patch(solve_path('get_db')) def test_delete_state_ok(self, mock: Mock): - self.mongo_crud.insert(self.model_example) - result = self.mongo_crud.delete(self._id_example) + self.mongo_crud_mock.insert(self.model_example) + result = self.mongo_crud_mock.delete(self._id_example) self.assertIsNotNone(result) self.assertIsInstance(result, DeleteResult) From 1a155cceb2a8bbfa313afdff62ea8b2a03385e2b Mon Sep 17 00:00:00 2001 From: Juan Esteban Aristizabal Date: Fri, 23 Apr 2021 17:57:17 -0500 Subject: [PATCH 34/42] chore: updated requirements.txt --- requirements.txt | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/requirements.txt b/requirements.txt index 28eeb99..7fbf992 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,15 @@ -atomicwrites==1.4.0 -attrs==20.3.0 -autopep8==1.5.6 +# +# These requirements were autogenerated by pipenv +# To regenerate from the project's Pipfile, run: +# +# pipenv lock --requirements --dev +# + +# Note: in pipenv 2020.x, "--dev" changed to emit both default and development +# requirements. To emit only development requirements, pass "--dev-only". + +-i https://pypi.org/simple +attrs==20.3.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3' certifi==2020.12.5 chardet==4.0.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' click==7.1.2; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' @@ -31,6 +40,5 @@ sortedcontainers==2.3.0 starlette==0.13.6; python_version >= '3.6' toml==0.10.2; python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3' typing-extensions==3.7.4.3 -urllib3==1.26.4 +urllib3==1.26.4; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4' uvicorn==0.13.4 -wincertstore==0.2 From 7c416b0adb30bf25dacd495ca61640c72c7c74c3 Mon Sep 17 00:00:00 2001 From: Juan Esteban Aristizabal Date: Fri, 23 Apr 2021 18:47:08 -0500 Subject: [PATCH 35/42] fix: style --- src/config.py | 4 ++-- src/interfaces/cmodels.py | 5 ++++- src/models/db/simulations.py | 2 +- .../interfaces/{test_cmodel_interface.py => test_cmodels.py} | 0 tests/interfaces/{test_crud_interface.py => test_crud.py} | 0 5 files changed, 7 insertions(+), 4 deletions(-) rename tests/interfaces/{test_cmodel_interface.py => test_cmodels.py} (100%) rename tests/interfaces/{test_crud_interface.py => test_crud.py} (100%) diff --git a/src/config.py b/src/config.py index 82e0d82..f2eb157 100644 --- a/src/config.py +++ b/src/config.py @@ -1,5 +1,5 @@ from dotenv import dotenv_values -settings = dotenv_values(".env") -db_config = dotenv_values(".db_config") +settings = dotenv_values('.env') +db_config = dotenv_values('.db_config') diff --git a/src/interfaces/cmodels.py b/src/interfaces/cmodels.py index 976e992..6addfc2 100644 --- a/src/interfaces/cmodels.py +++ b/src/interfaces/cmodels.py @@ -49,7 +49,10 @@ def insert_one_cmodel_document(self, model: CompartmentalModel): return model_inserted def insert_all_cmodel_documents(self): - return [self.insert_one_cmodel_document(model) for model in CompartmentalModelEnum.values()] + return [ + self.insert_one_cmodel_document(model) + for model in CompartmentalModelEnum.values() + ] @staticmethod def _prune_db_document(model_in_db: dict) -> dict: diff --git a/src/models/db/simulations.py b/src/models/db/simulations.py index 39321fa..8a681b7 100644 --- a/src/models/db/simulations.py +++ b/src/models/db/simulations.py @@ -2,7 +2,7 @@ from datetime import datetime from pydantic import BaseModel -from.base_model import PydanticObjectId +from .base_model import PydanticObjectId class SimulationType(str, Enum): diff --git a/tests/interfaces/test_cmodel_interface.py b/tests/interfaces/test_cmodels.py similarity index 100% rename from tests/interfaces/test_cmodel_interface.py rename to tests/interfaces/test_cmodels.py diff --git a/tests/interfaces/test_crud_interface.py b/tests/interfaces/test_crud.py similarity index 100% rename from tests/interfaces/test_crud_interface.py rename to tests/interfaces/test_crud.py From 88b970d3bfda8dbad965e4072a2a09f4bd5954ad Mon Sep 17 00:00:00 2001 From: Juan Esteban Aristizabal Date: Fri, 23 Apr 2021 18:49:05 -0500 Subject: [PATCH 36/42] fix: style src/models/db/cmodels.py --- src/models/db/cmodels.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/models/db/cmodels.py b/src/models/db/cmodels.py index 8a6b17f..e438595 100644 --- a/src/models/db/cmodels.py +++ b/src/models/db/cmodels.py @@ -1,7 +1,7 @@ from enum import Enum from typing import Dict, List -from bson.objectid import ObjectId +from bson.objectid import ObjectId from pydantic import BaseModel from .base_model import MetadataBaseDoc From 27dcb686b3cc391a716592290360f934c17a7fa9 Mon Sep 17 00:00:00 2001 From: Juan Esteban Aristizabal Date: Fri, 23 Apr 2021 18:53:34 -0500 Subject: [PATCH 37/42] deleted src/models/db/cmodels.AllCModels (not needed) --- src/models/db/__init__.py | 2 -- src/models/db/cmodels.py | 6 ------ tests/models/db/test_cmodels.py | 9 +-------- 3 files changed, 1 insertion(+), 16 deletions(-) diff --git a/src/models/db/__init__.py b/src/models/db/__init__.py index 94764d2..04752da 100644 --- a/src/models/db/__init__.py +++ b/src/models/db/__init__.py @@ -2,12 +2,10 @@ CompartmentalModelBase, CompartmentalModel, CModel, - AllCModels ) __all__ = [ 'CompartmentalModelBase', 'CompartmentalModel', 'CModel', - 'AllCModels' ] diff --git a/src/models/db/cmodels.py b/src/models/db/cmodels.py index 8a6b17f..7a0c8d6 100644 --- a/src/models/db/cmodels.py +++ b/src/models/db/cmodels.py @@ -93,9 +93,3 @@ def values(cls) -> List[CompartmentalModel]: class CModel(BaseModel): model: CompartmentalModelEnum - - -class AllCModels(BaseModel): - models: List[CompartmentalModelEnum] = [ - model.value for model in CompartmentalModelEnum - ] diff --git a/tests/models/db/test_cmodels.py b/tests/models/db/test_cmodels.py index cfc938e..59ee36e 100644 --- a/tests/models/db/test_cmodels.py +++ b/tests/models/db/test_cmodels.py @@ -4,8 +4,7 @@ from src.models.db.cmodels import ( CompartmentalModelBase, CompartmentalModelEnum, - CModel, - AllCModels, + CModel ) @@ -26,9 +25,3 @@ def test_CompartmentalModel(model: CompartmentalModelEnum): @given(st.builds(CModel)) def test_CModel(instance: CModel): assert isinstance(instance.model, CompartmentalModelEnum) - - -@given(st.builds(AllCModels)) -def test_AllCmodels(instance: AllCModels): - for model in instance.models: - assert isinstance(model, CompartmentalModelBase) From 29c836f8dd842869d8970ba8bdabc39e0ff40ebe Mon Sep 17 00:00:00 2001 From: Juan Esteban Aristizabal Date: Fri, 23 Apr 2021 18:55:06 -0500 Subject: [PATCH 38/42] fix: style in tests/interfaces/test_cmodels.py --- tests/interfaces/test_cmodels.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/interfaces/test_cmodels.py b/tests/interfaces/test_cmodels.py index 3d6c36a..42943c2 100644 --- a/tests/interfaces/test_cmodels.py +++ b/tests/interfaces/test_cmodels.py @@ -6,9 +6,7 @@ from pymongo.results import InsertOneResult from src.interfaces.cmodels import CModelsInterface -from src.models.db.cmodels import ( - CompartmentalModelEnum -) +from src.models.db.cmodels import CompartmentalModelEnum def solve_path(path: str): From aef1e2263449fed1bea8a67588eddbcdb1b9904d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Pe=C3=B1a?= Date: Sat, 24 Apr 2021 08:34:40 -0500 Subject: [PATCH 39/42] Chore: Update Singleton Pattern Chore: Update Container --- Dockerfile | 20 +++++++ docker-compose.yml | 32 ++++++++++++ docker-entrypoint.sh | 7 +++ main.py | 2 + requirements.txt | 2 + src/api.py | 10 ++-- src/db/mongo.py | 110 +++++++++++++++++++-------------------- src/interfaces/crud.py | 2 +- src/routers/main.py | 1 + src/use_cases/cmodels.py | 4 +- src/utils/patterns.py | 7 +++ 11 files changed, 131 insertions(+), 66 deletions(-) create mode 100644 Dockerfile create mode 100644 docker-compose.yml create mode 100644 docker-entrypoint.sh create mode 100644 src/utils/patterns.py diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..b0c4b53 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,20 @@ +FROM python:3.8-slim-buster +EXPOSE 8080 + +ENV PYTHONUNBUFFERED 1 +ENV APP_HOME /app + +COPY requirements.txt . + +RUN pip install --no-cache-dir -U pip \ + && pip install --no-cache-dir -r requirements.txt + +WORKDIR ${APP_HOME} + +COPY src src +COPY main.py . +COPY docker-entrypoint.sh . +COPY .env . + +ENTRYPOINT ["/bin/bash"] +CMD ["./docker-entrypoint.sh"] diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..49a1fb0 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,32 @@ +version: "3.5" +services: + user_app: + build: . + container_name: cdslab_models_app + env_file: .env + image: cdslab_user + networks: + - cdslab_models + ports: + - 5000:5000 + volumes: + - ./src:/app/src + + user_mongo: + container_name: cdslab_models_mongo + environment: + MONGO_INITDB_ROOT_USERNAME: cdsuser + MONGO_INITDB_ROOT_PASSWORD: cdspass + image: mongo:3-xenial + networks: + - cdslab_models + ports: + - 27017:27017 + volumes: + - /tmp/data/cdslab_models/:/data/db + + +networks: + cdslab_models: + name: cdslab_user + driver: bridge \ No newline at end of file diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh new file mode 100644 index 0000000..b1b4673 --- /dev/null +++ b/docker-entrypoint.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +if [ -z "${PORT}" ] ; then PORT=8080; fi + +if [ -z "${HOST}" ] ; then HOST=0.0.0.0; fi + +uvicorn main:app --port=${PORT} --host=${HOST} --reload diff --git a/main.py b/main.py index 43f798d..2286ecf 100644 --- a/main.py +++ b/main.py @@ -1,8 +1,10 @@ import uvicorn +from src.api import app from src.config import settings from src.use_cases.cmodels import CmodelUseCases +__all__ = ['app'] if __name__ == '__main__': CmodelUseCases.update_cmodels_collection() diff --git a/requirements.txt b/requirements.txt index 28eeb99..b345f8c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -14,6 +14,7 @@ iniconfig==1.1.1 mccabe==0.6.1 mongomock==3.22.1 packaging==20.9; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3' +pandas==1.2.4 pluggy==0.13.1; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3' py==1.10.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3' pycodestyle==2.7.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3' @@ -24,6 +25,7 @@ pyparsing==2.4.7; python_version >= '2.6' and python_version not in '3.0, 3.1, 3 pytest-cov==2.11.1 pytest==6.2.3 python-dotenv==0.17.0 +python-multipart==0.0.5 requests==2.25.1 sentinels==1.0.0 six==1.15.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3' diff --git a/src/api.py b/src/api.py index 560b554..a7b63df 100644 --- a/src/api.py +++ b/src/api.py @@ -5,21 +5,19 @@ from src.config import settings from src.routers.main import main_router - app = FastAPI() - app.add_middleware( TrustedHostMiddleware, - allowed_hosts=settings["ALLOWED_HOSTS"].split(",") + allowed_hosts=settings.get("ALLOWED_HOSTS", "*").split(",") ) app.add_middleware( CORSMiddleware, allow_credentials=True, - allow_origins=settings["ALLOWED_ORIGINS"].split(","), - allow_methods=settings["ALLOWED_METHODS"].split(","), - allow_headers=settings["ALLOWED_HEADERS"].split(",") + allow_origins=settings.get("ALLOWED_ORIGINS", "*").split(","), + allow_methods=settings.get("ALLOWED_METHODS", "*").split(","), + allow_headers=settings.get("ALLOWED_HEADERS", "*").split(",") ) app.include_router( diff --git a/src/db/mongo.py b/src/db/mongo.py index bfb4eb9..94abef1 100644 --- a/src/db/mongo.py +++ b/src/db/mongo.py @@ -1,63 +1,59 @@ from typing import Generator, Tuple from pymongo import MongoClient -from pymongo.database import Database from pymongo.collection import Collection +from pymongo.database import Database from src.config import db_config - - -def api_get_db_connection( - db_uri: str = db_config['MONGO_URI'], -) -> Generator[MongoClient, None, None]: - """Creates connection to Mongodb server. - - Yields - ------ - db_connection: MongoClient - Object containing the db connection. - """ - db_connection = MongoClient(db_uri) - - try: - yield db_connection - finally: - db_connection.close() - - -def get_db( - db_uri: str = db_config['MONGO_URI'], - db_name: str = db_config['MONGO_DB'] -) -> Tuple[MongoClient, Database]: - """Gets Mongodb connection and database. - - Parameters - ---------- - db_uri: str - Mongo server URI. - db_name: str - Mongo database name. - - Returns - ------- - db_connection : MongoClient - db : pymongo.database.Database - """ - db_connection = MongoClient(db_uri) - db: Database = db_connection[db_name] - - return db_connection, db - - -def get_collection( - coll_name: str = db_config['CMODELS_COLL'] -) -> Tuple[MongoClient, Collection]: - """ - Returns - ------- - db_connection: pymongo.MongoClient - coll: pymongo.collection.Collection - """ - db_connection, db = get_db() - coll: Collection = db[coll_name] - return db_connection, coll +from src.utils.patterns import Singleton + + +class MongoConnection(metaclass=Singleton): + db_uri = db_config.get('MONGO_URI') + db_name = db_config.get('MONGO_DB') + collection_name = db_config.get('CMODELS_COLL') + + def api_get_db_connection(self) -> Generator[MongoClient, None, None]: + """Creates connection to Mongodb server. + + Yields + ------ + db_connection: MongoClient + Object containing the db connection. + """ + db_connection = MongoClient(self.db_uri) + + try: + yield db_connection + finally: + db_connection.close() + + def get_db(self) -> Tuple[MongoClient, Database]: + """Gets Mongodb connection and database. + + Returns + ------- + db_connection : MongoClient + db : pymongo.database.Database + """ + db_connection = MongoClient(self.db_uri) + db: Database = db_connection[self.db_name] + + return db_connection, db + + def get_collection( + self, + collection_name: str = None + ) -> Tuple[MongoClient, Collection]: + """ + Returns + ------- + db_connection: pymongo.MongoClient + coll: pymongo.collection.Collection + """ + if not collection_name: + collection_name = self.collection_name + pass + db_connection, db = self.get_db() + coll: Collection = db[collection_name] + return db_connection, coll diff --git a/src/interfaces/crud.py b/src/interfaces/crud.py index 94ce9df..e6b1c49 100644 --- a/src/interfaces/crud.py +++ b/src/interfaces/crud.py @@ -4,7 +4,7 @@ from pymongo.mongo_client import MongoClient -class MongoCRUD(): +class MongoCRUD: def __init__( self, db_connection: MongoClient, diff --git a/src/routers/main.py b/src/routers/main.py index ad0f8ce..afe7844 100644 --- a/src/routers/main.py +++ b/src/routers/main.py @@ -3,6 +3,7 @@ main_router_prefix = "" main_router = APIRouter(prefix=main_router_prefix) + @main_router.get('/') async def hello(request: Request): return {'hello': 'world'} diff --git a/src/use_cases/cmodels.py b/src/use_cases/cmodels.py index 3db5767..454a910 100644 --- a/src/use_cases/cmodels.py +++ b/src/use_cases/cmodels.py @@ -1,10 +1,10 @@ -from src.db.mongo import get_collection +from src.db.mongo import MongoConnection from src.interfaces.cmodels import CModelsInterface class CmodelUseCases: @staticmethod def update_cmodels_collection(): - db_connection, cmodels_coll = get_collection() + db_connection, cmodels_coll = MongoConnection().get_collection() cmodels_interface = CModelsInterface(db_connection, cmodels_coll) cmodels_interface.insert_all_cmodel_documents() diff --git a/src/utils/patterns.py b/src/utils/patterns.py new file mode 100644 index 0000000..3776cb9 --- /dev/null +++ b/src/utils/patterns.py @@ -0,0 +1,7 @@ +class Singleton(type): + _instances = {} + + def __call__(cls, *args, **kwargs): + if cls not in cls._instances: + cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs) + return cls._instances[cls] From 7095f08580d21e8b1afbc331e1348fa92b26fd11 Mon Sep 17 00:00:00 2001 From: Juan Esteban Aristizabal Date: Sat, 24 Apr 2021 14:43:04 -0500 Subject: [PATCH 40/42] fix: test cases run appropriately --- main.py | 4 +++- src/db/mongo.py | 2 +- src/interfaces/cmodels.py | 10 ++++------ src/interfaces/crud.py | 10 ++++------ src/models/db/cmodels.py | 1 + src/use_cases/cmodels.py | 5 ++--- tests/db/test_mongo.py | 14 ++++++-------- tests/interfaces/test_cmodels.py | 9 ++++++++- tests/interfaces/test_crud.py | 9 ++++++++- tests/use_cases/test_cmodels.py | 6 +++--- 10 files changed, 40 insertions(+), 30 deletions(-) diff --git a/main.py b/main.py index 82a1b09..d93bb78 100644 --- a/main.py +++ b/main.py @@ -1,3 +1,4 @@ +from src.db.mongo import MongoClientSingleton import uvicorn from src.api import app @@ -7,7 +8,8 @@ __all__ = ['app'] if __name__ == '__main__': - CmodelUseCases.update_cmodels_collection() + mongo_singleton = MongoClientSingleton() + CmodelUseCases(mongo_singleton).update_cmodels_collection() uvicorn.run( "src.api:app", host=settings.get('HOST'), diff --git a/src/db/mongo.py b/src/db/mongo.py index b4fc5ca..af701c5 100644 --- a/src/db/mongo.py +++ b/src/db/mongo.py @@ -70,7 +70,7 @@ def db(self): return self._db @db.setter - def db(self, value): # noqa + def db(self, value): # noqa if isinstance(value, str): self._db = self.db_connection[value] elif isinstance(value, Database) or isinstance(value, mongomock.Database): diff --git a/src/interfaces/cmodels.py b/src/interfaces/cmodels.py index 6addfc2..1f561a7 100644 --- a/src/interfaces/cmodels.py +++ b/src/interfaces/cmodels.py @@ -1,8 +1,6 @@ from datetime import datetime -from pymongo.collection import Collection -from pymongo.mongo_client import MongoClient - +from src.db.mongo import MongoClientSingleton from src.models.db.cmodels import ( CompartmentalModel, CompartmentalModelEnum @@ -14,10 +12,10 @@ class CModelsInterface: def __init__( self, - db_connection: MongoClient, - collection: Collection + mongo_singleton: MongoClientSingleton ) -> None: - self.crud = MongoCRUD(db_connection, collection) + self.mongo_singleton = mongo_singleton + self.crud = MongoCRUD(self.mongo_singleton) def insert_one_cmodel_document(self, model: CompartmentalModel): diff --git a/src/interfaces/crud.py b/src/interfaces/crud.py index 5618862..91522bd 100644 --- a/src/interfaces/crud.py +++ b/src/interfaces/crud.py @@ -1,18 +1,16 @@ from typing import Any, Union from bson.objectid import ObjectId -from pymongo.collection import Collection -from pymongo.mongo_client import MongoClient +from src.db.mongo import MongoClientSingleton class MongoCRUD: def __init__( self, - db_connection: MongoClient, - collection: Collection + mongo_singleton: MongoClientSingleton ) -> None: - self.db_connection = db_connection - self.collection = collection + self.mongo_singleton = mongo_singleton + self.db_connection, self.collection = self.mongo_singleton.get_collection() def insert(self, document: dict): """ diff --git a/src/models/db/cmodels.py b/src/models/db/cmodels.py index 1792734..cc243cf 100644 --- a/src/models/db/cmodels.py +++ b/src/models/db/cmodels.py @@ -6,6 +6,7 @@ from .base_model import MetadataBaseDoc + class CompartmentalModelBase(BaseModel): """Base Model for Compartmental Models """ diff --git a/src/use_cases/cmodels.py b/src/use_cases/cmodels.py index 2035d63..4661c68 100644 --- a/src/use_cases/cmodels.py +++ b/src/use_cases/cmodels.py @@ -6,11 +6,10 @@ class CmodelUseCases: def __init__( self, - mongo_singleton: MongoClientSingleton = MongoClientSingleton() + mongo_singleton: MongoClientSingleton ) -> None: self.mongo_singleton = mongo_singleton - self.db_connection, self.cmodels_coll = self.mongo_singleton.get_collection() - self.cmodels_interface = CModelsInterface(self.db_connection, self.cmodels_coll) + self.cmodels_interface = CModelsInterface(self.mongo_singleton) def update_cmodels_collection(self) -> None: self.cmodels_interface.insert_all_cmodel_documents() diff --git a/tests/db/test_mongo.py b/tests/db/test_mongo.py index 4cbc3eb..3e567b9 100644 --- a/tests/db/test_mongo.py +++ b/tests/db/test_mongo.py @@ -3,9 +3,6 @@ import mongomock from mongomock import patch as db_path -from pymongo.collection import Collection -from pymongo.database import Database -from pymongo.mongo_client import MongoClient from src.db.mongo import MongoClientSingleton @@ -27,6 +24,7 @@ def setUp(self): ) def tearDown(self): + self.collection_mock.drop() self.connection_mock.close() @patch(solve_path('db_config')) @@ -34,7 +32,7 @@ def test_get_db_connection(self, mock: Mock): db_connection_gen = self.mongo_singleton_mock.api_get_db_connection() db_connection = next(db_connection_gen) - self.assertIsInstance(db_connection, MongoClient) + self.assertIsInstance(db_connection, mongomock.MongoClient) with self.assertRaises(StopIteration): next(db_connection_gen) @@ -45,13 +43,13 @@ def test_get_db(self, mock_config: Mock): "test_db" ) - self.assertIsInstance(db_connection, MongoClient) - self.assertIsInstance(db, Database) + self.assertIsInstance(db_connection, mongomock.MongoClient) + self.assertIsInstance(db, mongomock.Database) @patch(solve_path('db_config')) def test_get_collection(self, mock_config: Mock): db_connection, coll = self.mongo_singleton_mock.get_collection() - self.assertIsInstance(db_connection, MongoClient) - self.assertIsInstance(coll, Collection) + self.assertIsInstance(db_connection, mongomock.MongoClient) + self.assertIsInstance(coll, mongomock.Collection) diff --git a/tests/interfaces/test_cmodels.py b/tests/interfaces/test_cmodels.py index e4127f1..99d74f7 100644 --- a/tests/interfaces/test_cmodels.py +++ b/tests/interfaces/test_cmodels.py @@ -7,6 +7,7 @@ from src.interfaces.cmodels import CModelsInterface from src.models.db.cmodels import CompartmentalModelEnum +from src.db.mongo import MongoClientSingleton def solve_path(path: str): @@ -21,12 +22,18 @@ def setUp(self): self.connection_mock = mongomock.MongoClient('server.example.com') self.db_mock = self.connection_mock.db self.collection_mock = self.db_mock.collection + self.mongo_singleton_mock = MongoClientSingleton( + db_connection=self.connection_mock, + db=self.db_mock, + coll=self.collection_mock + ) self.cmodels_interface_mock = CModelsInterface( - self.connection_mock, self.collection_mock + self.mongo_singleton_mock ) self.cmodel_example_document = CompartmentalModelEnum.sir.value def tearDown(self): + self.mongo_singleton_mock.coll.drop() self.connection_mock.close() @patch(solve_path('db_config')) diff --git a/tests/interfaces/test_crud.py b/tests/interfaces/test_crud.py index 242a093..7c3368c 100644 --- a/tests/interfaces/test_crud.py +++ b/tests/interfaces/test_crud.py @@ -7,6 +7,7 @@ from src.interfaces.crud import MongoCRUD from src.models.db.cmodels import CompartmentalModelEnum +from src.db.mongo import MongoClientSingleton def solve_path(path: str): @@ -21,11 +22,17 @@ def setUp(self): self.connection_mock = mongomock.MongoClient('server.example.com') self.db_mock = self.connection_mock.db self.collection_mock = self.db_mock.collection - self.mongo_crud_mock = MongoCRUD(self.connection_mock, self.collection_mock) + self.mongo_singleton_mock = MongoClientSingleton( + db_connection=self.connection_mock, + db=self.db_mock, + coll=self.collection_mock + ) + self.mongo_crud_mock = MongoCRUD(self.mongo_singleton_mock) self._id_example = CompartmentalModelEnum.sir.value.id self.model_example = {'_id': self._id_example} def tearDown(self): + self.mongo_singleton_mock.coll.drop() self.connection_mock.close() @patch(solve_path('db_config')) diff --git a/tests/use_cases/test_cmodels.py b/tests/use_cases/test_cmodels.py index b229554..65701a7 100644 --- a/tests/use_cases/test_cmodels.py +++ b/tests/use_cases/test_cmodels.py @@ -1,5 +1,3 @@ - -from src.db.mongo import MongoClientSingleton from unittest import TestCase from unittest.mock import patch, Mock @@ -9,6 +7,7 @@ from src.use_cases.cmodels import CmodelUseCases from src.interfaces.crud import MongoCRUD from src.models.db.cmodels import CompartmentalModelEnum +from src.db.mongo import MongoClientSingleton class CmodelUseCasesTestCase(TestCase): @@ -24,11 +23,12 @@ def setUp(self): coll=self.collection_mock ) self.mongo_crud_mock = MongoCRUD( - *self.mongo_singleton_mock.get_collection() + self.mongo_singleton_mock ) self.cmodel_use_cases = CmodelUseCases(self.mongo_singleton_mock) def tearDown(self): + self.collection_mock.drop() self.connection_mock.close() @patch('src.config.db_config') From 31ecb49858b529373a258fca7e7a28eec189cd35 Mon Sep 17 00:00:00 2001 From: Juan Esteban Aristizabal Date: Sat, 24 Apr 2021 14:52:31 -0500 Subject: [PATCH 41/42] fix: type hint --- src/db/mongo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/db/mongo.py b/src/db/mongo.py index af701c5..f86262f 100644 --- a/src/db/mongo.py +++ b/src/db/mongo.py @@ -10,7 +10,7 @@ class MongoClientSingleton(metaclass=Singleton): - db_uri: str = db_config.get('MONGO_URI'), + db_uri: Optional[str] = db_config.get('MONGO_URI'), def __init__( self, From 930fbdbb23065410b9e1d87f5066d945744252bc Mon Sep 17 00:00:00 2001 From: Juan Esteban Aristizabal Date: Sun, 25 Apr 2021 00:46:09 -0500 Subject: [PATCH 42/42] fix: typo and setters' logic in src.db.mongo --- src/db/mongo.py | 9 ++++----- tests/db/test_mongo.py | 4 +++- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/db/mongo.py b/src/db/mongo.py index f86262f..f4d81df 100644 --- a/src/db/mongo.py +++ b/src/db/mongo.py @@ -37,7 +37,7 @@ def api_get_db_connection(self) -> Generator[MongoClient, None, None]: def get_db( self, - db: Optional[Union[str, Database]] = None + db: Optional[str] = None ) -> Tuple[MongoClient, Database]: """Gets Mongodb connection and database. @@ -46,14 +46,13 @@ def get_db( db_connection : MongoClient db : pymongo.database.Database """ - if db: + if db and isinstance(db, str): self.db = db return self.db_connection, self.db def get_collection( self, - db_name: str = db_config.get('MONGO_DB'), - coll: Optional[Union[str, Database]] = None + coll: Optional[str] = None ) -> Tuple[MongoClient, Collection]: """ Returns @@ -61,7 +60,7 @@ def get_collection( db_connection: pymongo.MongoClient coll: pymongo.collection.Collection """ - if coll: + if coll and isinstance(coll, str): self.coll = coll return self.db_connection, self.coll diff --git a/tests/db/test_mongo.py b/tests/db/test_mongo.py index 3e567b9..7d7caae 100644 --- a/tests/db/test_mongo.py +++ b/tests/db/test_mongo.py @@ -49,7 +49,9 @@ def test_get_db(self, mock_config: Mock): @patch(solve_path('db_config')) def test_get_collection(self, mock_config: Mock): - db_connection, coll = self.mongo_singleton_mock.get_collection() + db_connection, coll = self.mongo_singleton_mock.get_collection( + 'test_coll' + ) self.assertIsInstance(db_connection, mongomock.MongoClient) self.assertIsInstance(coll, mongomock.Collection)