diff --git a/.gitignore b/.gitignore index 80c67e2e..55f6bbfb 100644 --- a/.gitignore +++ b/.gitignore @@ -38,8 +38,4 @@ migration_status.csv .DS_Store # windows env variables -.env.bat -# mac / linux env variables -.env -#PowerShell env variables -.env.ps1 +env.* diff --git a/tests/utils/extend_model_test.py b/tests/utils/extend_model_test.py new file mode 100644 index 00000000..09a2733b --- /dev/null +++ b/tests/utils/extend_model_test.py @@ -0,0 +1,32 @@ +from unittest.mock import patch +from utils.extend_model import add_custom_attribute + + +@patch('time_tracker_api.project_types.project_types_model.create_dao') +@patch('time_tracker_api.customers.customers_model.create_dao') +def test_add_custom_attribute(customers_create_dao, projects_create_dao): + @add_custom_attribute('project_type', projects_create_dao) + @add_custom_attribute('customer', customers_create_dao) + @patch('time_tracker_api.projects.projects_model.ProjectCosmosDBModel') + def fn(project): + project.return_value.name = "Franklin, Mcdonald and Morrison" + project.return_value.description = "Include speech feeling court almost country smile economy. True quality mention key. Similar provide yard." + project.return_value.customer_id = ( + "9afbfa3a-9de4-4b90-a1b7-a53d2c17a178" + ) + project.return_value.project_type_id = ( + "208aadb7-1ec1-4a67-a0b0-e0308d27045b" + ) + project.return_value.technologies = "['python', 'restplus', 'openapi']" + project.return_value.status = "active" + project.return_value.customer_name = "Tucker Inc" + project.return_value.id = "768c924e-4501-457f-99c5-7198440d3c60" + project.return_value.tenant_id = "e2953984-03e7-4730-be29-1753d24df3b0" + project.return_value.deleted = None + + return project.return_value + + project = fn().__dict__ + + assert 'customer' in project + assert 'project_type' in project diff --git a/time_tracker_api/projects/projects_model.py b/time_tracker_api/projects/projects_model.py index 4b3b77ab..4ee44375 100644 --- a/time_tracker_api/projects/projects_model.py +++ b/time_tracker_api/projects/projects_model.py @@ -11,9 +11,16 @@ from time_tracker_api.customers.customers_model import ( create_dao as customers_create_dao, ) +from time_tracker_api.project_types.project_types_model import ( + create_dao as project_types_create_dao, +) from time_tracker_api.customers.customers_model import CustomerCosmosDBModel from utils.query_builder import CosmosDBQueryBuilder -from utils.extend_model import add_customer_name_to_projects +from utils.extend_model import ( + add_customer_name_to_projects, + add_custom_attribute_in_list, + add_custom_attribute, +) class ProjectDao(CRUDDao): @@ -101,6 +108,17 @@ class ProjectCosmosDBDao(APICosmosDBDao, ProjectDao): def __init__(self, repository): CosmosDBDao.__init__(self, repository) + @add_custom_attribute('customer', customers_create_dao) + @add_custom_attribute('project_type', project_types_create_dao) + def get(self, id) -> ProjectCosmosDBModel: + """ + Get one project an active client + :param (str) id: project's id + """ + return super().get(id) + + @add_custom_attribute_in_list('customer', customers_create_dao) + @add_custom_attribute_in_list('project_type', project_types_create_dao) def get_all( self, conditions: dict = None, project_ids: List = None, **kwargs ) -> list: diff --git a/time_tracker_api/projects/projects_namespace.py b/time_tracker_api/projects/projects_namespace.py index 3757b878..82dbaf2b 100644 --- a/time_tracker_api/projects/projects_namespace.py +++ b/time_tracker_api/projects/projects_namespace.py @@ -70,7 +70,42 @@ }, ) +project_type_nested_field = ns.model('ProjectType', { + 'name': fields.String( + title='Name', + required=True, + max_length=50, + description='Name of the project type', + example=faker.random_element(["Customer", "Training", "Internal"]), + ), + 'description': NullableString( + title='Description', + required=False, + max_length=250, + description='Comments about the project type', + example=faker.paragraph(), + ) +}) + +customer_nested_field = ns.model('Customer', { + 'name': fields.String( + title='Name', + required=True, + max_length=50, + description='Name of the customer', + example=faker.company(), + ), + 'description': NullableString( + title='Description', + required=False, + max_length=250, + description='Description about the customer', + example=faker.paragraph(), + ) +}) + project_response_fields = { + # TODO: Remove this DEAD CODE 'customer_name': fields.String( required=True, title='Customer Name', @@ -78,6 +113,8 @@ description='Name of the customer of the project', example=faker.company(), ), + 'customer': fields.Nested(customer_nested_field), + 'project_type': fields.Nested(project_type_nested_field), } project_response_fields.update(common_fields) diff --git a/utils/extend_model.py b/utils/extend_model.py index b13faa44..ce39d5b7 100644 --- a/utils/extend_model.py +++ b/utils/extend_model.py @@ -1,6 +1,64 @@ +from functools import wraps import re +def add_custom_attribute(attr, dao): + """ + Decorator to add an custom attribute in model, based on entity's id + :param (attr) attribute: name of the new attribute + :param (dao) dao: related entity to the model + """ + + def decorator_for_single_item(func): + @wraps(func) + def wrapper(*args, **kwargs): + current_dao = dao() + entity_model = func(*args, **kwargs) + attribute_id = f"{attr}_id" + + if entity_model and attribute_id in entity_model.__dict__: + value_id = entity_model.__dict__[attribute_id] + if value_id: + related_entity = current_dao.get(value_id) + setattr(entity_model, attr, related_entity) + + return entity_model + + return wrapper + + return decorator_for_single_item + + +def add_custom_attribute_in_list(attr, dao): + """ + Decorator to add an custom attribute in model_list, based on entity's id + :param (attr) attribute: name of the new attribute + :param (dao) dao: related entity to the model + """ + + def decorator_for_list_item(func): + @wraps(func) + def wrapper(*args, **kwargs): + current_dao = dao() + entity_model_list = func(*args, **kwargs) + attribute_id = f"{attr}_id" + + related_entity_list = current_dao.get_all() + related_entities_ids_dict = {x.id: x for x in related_entity_list} + + for entity_model in entity_model_list: + value_id = entity_model.__dict__[attribute_id] + setattr( + entity_model, attr, related_entities_ids_dict.get(value_id) + ) + + return entity_model_list + + return wrapper + + return decorator_for_list_item + + def add_customer_name_to_projects(projects, customers): """ Add attribute customer_name in project model, based on customer_id of the