From 732d19d0d6e29dc60c04989c78ac3d6a9dd0488d Mon Sep 17 00:00:00 2001 From: Pablo Date: Tue, 27 Apr 2021 15:04:48 -0500 Subject: [PATCH 1/2] fix: TT-219 Refactoring find_all for time entries --- .../time_entries/time_entries_model_test.py | 49 ++++++++- .../time_entries_namespace_test.py | 61 +++++++++- .../time_entries_query_builder_test.py | 10 +- .../time_entries/time_entries_dao.py | 46 +++++++- .../time_entries/time_entries_namespace.py | 8 +- .../time_entries_query_builder.py | 3 +- .../time_entries/time_entries_repository.py | 104 ++++++++++++++---- 7 files changed, 244 insertions(+), 37 deletions(-) diff --git a/tests/time_tracker_api/time_entries/time_entries_model_test.py b/tests/time_tracker_api/time_entries/time_entries_model_test.py index 3c0e4fd6..be74746d 100644 --- a/tests/time_tracker_api/time_entries/time_entries_model_test.py +++ b/tests/time_tracker_api/time_entries/time_entries_model_test.py @@ -275,12 +275,41 @@ def test_updated_item_without_deleted_key_should_call_validate_data( @patch( 'time_tracker_api.time_entries.time_entries_repository.TimeEntryCosmosDBRepository.get_page_size_or' ) +@patch( + 'commons.data_access_layer.cosmos_db.CosmosDBRepository.generate_params' +) +@patch( + 'commons.data_access_layer.cosmos_db.CosmosDBRepository.create_sql_condition_for_visibility' +) +@patch( + 'commons.data_access_layer.cosmos_db.CosmosDBRepository.create_sql_where_conditions' +) +@patch( + 'commons.data_access_layer.cosmos_db.CosmosDBRepository.create_custom_sql_conditions' +) +@patch( + 'time_tracker_api.time_entries.time_entries_repository.TimeEntryCosmosDBRepository.add_complementary_info' +) +@patch( + 'commons.feature_toggles.feature_toggle_manager.FeatureToggleManager.get_azure_app_configuration_client' +) +@patch( + 'commons.feature_toggles.feature_toggle_manager.FeatureToggleManager.is_toggle_enabled_for_user' +) def test_find_all_v2( + is_toggle_enabled_for_user_mock, + get_azure_app_configuration_client_mock, + add_complementary_info_mock, + create_custom_sql_conditions_mock, + create_sql_where_conditions_mock, + create_sql_condition_for_visibility_mock, + generate_params_mock, get_page_size_or_mock, find_partition_key_value_mock, event_context: EventContext, time_entry_repository: TimeEntryCosmosDBRepository, ): + is_toggle_enabled_for_user_mock.return_value = True expected_item = { 'id': 'id', 'start_date': '2021-03-22T10:00:00.000Z', @@ -295,21 +324,29 @@ def test_find_all_v2( time_entry_repository.container = Mock() time_entry_repository.container.query_items = query_items_mock - result = time_entry_repository.find_all_v2( - event_context, - ['owner_id'], - { + time_entry_repository.add_complementary_info = Mock() + time_entry_repository.add_complementary_info = query_items_mock + + result = time_entry_repository.find_all( + conditions={"user_id": "*"}, + custom_sql_conditions=[], + event_context=event_context, + date_range={ 'start_date': "2021-03-22T10:00:00.000Z", 'end_date': "2021-03-22T11:00:00.000Z", }, + custom_params={}, ) find_partition_key_value_mock.assert_called_once() get_page_size_or_mock.assert_called_once() assert len(result) == 1 time_entry = result[0] - assert isinstance(time_entry, TimeEntryCosmosDBModel) - assert time_entry.__dict__ == expected_item + assert time_entry == expected_item + + create_sql_condition_for_visibility_mock.assert_called_once() + create_sql_where_conditions_mock.assert_called_once() + create_custom_sql_conditions_mock.assert_called_once() @patch( diff --git a/tests/time_tracker_api/time_entries/time_entries_namespace_test.py b/tests/time_tracker_api/time_entries/time_entries_namespace_test.py index ba3ccb95..0da2964c 100644 --- a/tests/time_tracker_api/time_entries/time_entries_namespace_test.py +++ b/tests/time_tracker_api/time_entries/time_entries_namespace_test.py @@ -137,12 +137,22 @@ def test_create_time_entry_with_missing_req_field_should_return_bad_request( repository_create_mock.assert_not_called() +@patch( + 'commons.feature_toggles.feature_toggle_manager.FeatureToggleManager.get_azure_app_configuration_client' +) +@patch( + 'commons.feature_toggles.feature_toggle_manager.FeatureToggleManager.is_toggle_enabled_for_user' +) def test_list_all_time_entries( + is_toggle_enabled_for_user_mock, + get_azure_app_configuration_client_mock, client: FlaskClient, mocker: MockFixture, valid_header: dict, time_entries_dao, ): + is_toggle_enabled_for_user_mock.return_value = True + dao_get_all_mock = mocker.patch.object( time_entries_dao, 'get_all', return_value=[] ) @@ -204,6 +214,14 @@ def test_get_time_entry_should_succeed_with_valid_id( 'time_tracker_api.time_entries.time_entries_dao.TimeEntriesCosmosDBDao.handle_date_filter_args', Mock(), ) +@patch( + 'time_tracker_api.time_entries.time_entries_repository.TimeEntryCosmosDBRepository.create_sql_date_range_filter', + Mock(), +) +@patch( + 'commons.data_access_layer.cosmos_db.CosmosDBRepository.generate_params', + Mock(), +) @patch('msal.ConfidentialClientApplication', Mock()) @patch('utils.azure_users.AzureConnection.get_token', Mock()) @patch('utils.azure_users.AzureConnection.is_test_user') @@ -211,11 +229,19 @@ def test_get_time_entry_should_succeed_with_valid_id( @pytest.mark.parametrize( 'current_user_is_tester, expected_user_ids', [ - (True, ['id1', 'id2']), - (False, ['id2']), + (True, ['id1', 'id1']), + (False, ['id1', 'id1']), ], ) +@patch( + 'commons.feature_toggles.feature_toggle_manager.FeatureToggleManager.get_azure_app_configuration_client' +) +@patch( + 'commons.feature_toggles.feature_toggle_manager.FeatureToggleManager.is_toggle_enabled_for_user' +) def test_get_time_entries_by_type_of_user( + is_toggle_enabled_for_user_mock, + get_azure_app_configuration_client_mock, get_test_user_ids_mock, is_test_user_mock, client: FlaskClient, @@ -224,6 +250,7 @@ def test_get_time_entries_by_type_of_user( current_user_is_tester, expected_user_ids, ): + is_toggle_enabled_for_user_mock.return_value = True test_user_id = "id1" non_test_user_id = "id2" te1 = TimeEntryCosmosDBModel( @@ -239,7 +266,7 @@ def test_get_time_entries_by_type_of_user( { "id": '2', "project_id": "2", - "owner_id": non_test_user_id, + "owner_id": test_user_id, "tenant_id": '2', "start_date": "", } @@ -690,12 +717,21 @@ def test_create_with_valid_uuid_format_should_return_created( ('/time-entries'), ], ) +@patch( + 'commons.feature_toggles.feature_toggle_manager.FeatureToggleManager.get_azure_app_configuration_client' +) +@patch( + 'commons.feature_toggles.feature_toggle_manager.FeatureToggleManager.is_toggle_enabled_for_user' +) def test_get_all_passes_date_range_built_from_params_to_find_all( + is_toggle_enabled_for_user_mock, + get_azure_app_configuration_client_mock, client: FlaskClient, valid_header: dict, url: str, time_entries_dao, ): + is_toggle_enabled_for_user_mock.return_value = True time_entries_dao.repository.find_all = Mock(return_value=[]) response = client.get(url, headers=valid_header) @@ -727,7 +763,15 @@ def test_get_all_passes_date_range_built_from_params_to_find_all( ), ], ) +@patch( + 'commons.feature_toggles.feature_toggle_manager.FeatureToggleManager.get_azure_app_configuration_client' +) +@patch( + 'commons.feature_toggles.feature_toggle_manager.FeatureToggleManager.is_toggle_enabled_for_user' +) def test_get_all_passes_date_range_to_find_all_with_default_tz_offset( + is_toggle_enabled_for_user_mock, + get_azure_app_configuration_client_mock, client: FlaskClient, valid_header: dict, time_entries_dao, @@ -735,6 +779,8 @@ def test_get_all_passes_date_range_to_find_all_with_default_tz_offset( start_date: str, end_date: str, ): + is_toggle_enabled_for_user_mock.return_value = True + time_entries_dao.repository.find_all = Mock(return_value=[]) response = client.get(url, headers=valid_header) @@ -788,7 +834,15 @@ def test_get_all_passes_date_range_to_find_all_with_default_tz_offset( ), ], ) +@patch( + 'commons.feature_toggles.feature_toggle_manager.FeatureToggleManager.get_azure_app_configuration_client' +) +@patch( + 'commons.feature_toggles.feature_toggle_manager.FeatureToggleManager.is_toggle_enabled_for_user' +) def test_get_all_passes_date_range_to_find_all_with_given_tz_offset( + is_toggle_enabled_for_user_mock, + get_azure_app_configuration_client_mock, client: FlaskClient, valid_header: dict, time_entries_dao, @@ -796,6 +850,7 @@ def test_get_all_passes_date_range_to_find_all_with_given_tz_offset( start_date: str, end_date: str, ): + is_toggle_enabled_for_user_mock.return_value = True time_entries_dao.repository.find_all = Mock(return_value=[]) response = client.get(url, headers=valid_header) diff --git a/tests/time_tracker_api/time_entries/time_entries_query_builder_test.py b/tests/time_tracker_api/time_entries/time_entries_query_builder_test.py index 88e7abb8..3e7fdc91 100644 --- a/tests/time_tracker_api/time_entries/time_entries_query_builder_test.py +++ b/tests/time_tracker_api/time_entries/time_entries_query_builder_test.py @@ -18,7 +18,10 @@ def test_TimeEntryQueryBuilder_is_subclass_CosmosDBQueryBuilder(): def test_add_sql_date_range_condition_should_update_where_list(): time_entry_query_builder = ( TimeEntryQueryBuilder().add_sql_date_range_condition( - ("2021-03-19T05:07:00.000Z", "2021-03-25T10:00:00.000Z") + { + "start_date": "2021-03-19T05:07:00.000Z", + "end_date": "2021-03-25T10:00:00.000Z", + } ) ) expected_params = [ @@ -34,7 +37,10 @@ def test_build_with_add_sql_date_range_condition(): time_entry_query_builder = ( TimeEntryQueryBuilder() .add_sql_date_range_condition( - ("2021-03-19T05:00:00.000Z", "2021-03-20T10:00:00.000Z") + { + "start_date": "2021-03-19T05:00:00.000Z", + "end_date": "2021-03-20T10:00:00.000Z", + } ) .build() ) diff --git a/time_tracker_api/time_entries/time_entries_dao.py b/time_tracker_api/time_entries/time_entries_dao.py index 32c7112a..807ebe70 100644 --- a/time_tracker_api/time_entries/time_entries_dao.py +++ b/time_tracker_api/time_entries/time_entries_dao.py @@ -2,6 +2,7 @@ from commons.data_access_layer.cosmos_db import ( CosmosDBDao, CustomError, + CosmosDBRepository, ) from utils.extend_model import ( add_project_info_to_time_entries, @@ -91,7 +92,7 @@ def build_custom_query(self, is_admin: bool, conditions: dict = None): ) return custom_query - def get_all(self, conditions: dict = None, **kwargs) -> list: + def get_all_old(self, conditions: dict = None, **kwargs) -> list: event_ctx = self.create_event_context("read-many") conditions.update({"owner_id": event_ctx.user_id}) is_complete_query = conditions.get("user_id") == '*' @@ -106,7 +107,7 @@ def get_all(self, conditions: dict = None, **kwargs) -> list: current_user_is_tester = azure_connection.is_test_user( event_ctx.user_id ) - time_entries_list = self.repository.find_all( + time_entries_list = self.repository.find_all_old( event_ctx, conditions=conditions, custom_sql_conditions=custom_query, @@ -124,6 +125,47 @@ def get_all(self, conditions: dict = None, **kwargs) -> list: else: return time_entries_list + def get_all(self, conditions: dict = None, **kwargs) -> list: + event_ctx = self.create_event_context("read-many") + conditions.update({"owner_id": event_ctx.user_id}) + is_complete_query = conditions.get("user_id") == '*' + custom_query = self.build_custom_query( + is_admin=event_ctx.is_admin, + conditions=conditions, + ) + date_range = self.handle_date_filter_args(args=conditions) + limit = conditions.get("limit", None) + conditions.pop("limit", None) + azure_connection = AzureConnection() + current_user_is_tester = azure_connection.is_test_user( + event_ctx.user_id + ) + + custom_query.append( + TimeEntryCosmosDBRepository.create_sql_date_range_filter( + date_range + ) + ) + custom_params = CosmosDBRepository.generate_params(date_range) + + test_user_ids = ( + azure_connection.get_test_user_ids() + if not current_user_is_tester and is_complete_query + else None + ) + + time_entries_list = self.repository.find_all( + conditions=conditions, + custom_sql_conditions=custom_query, + test_user_ids=test_user_ids, + date_range=date_range, + max_count=limit, + custom_params=custom_params, + event_context=event_ctx, + ) + + return time_entries_list + def get_lastest_entries_by_project( self, conditions: dict = None, **kwargs ) -> list: diff --git a/time_tracker_api/time_entries/time_entries_namespace.py b/time_tracker_api/time_entries/time_entries_namespace.py index 47a9a202..e9863a4b 100644 --- a/time_tracker_api/time_entries/time_entries_namespace.py +++ b/time_tracker_api/time_entries/time_entries_namespace.py @@ -19,6 +19,8 @@ ) from time_tracker_api.time_entries.time_entries_dao import create_dao +from commons.feature_toggles.feature_toggle_manager import FeatureToggleManager + faker = Faker() ns = api.namespace( @@ -237,7 +239,11 @@ class TimeEntries(Resource): def get(self): """List all time entries""" conditions = attributes_filter.parse_args() - return time_entries_dao.get_all(conditions=conditions) + find_all_version = FeatureToggleManager('find_all_version') + if find_all_version.is_toggle_enabled_for_user(): + return time_entries_dao.get_all(conditions=conditions) + else: + return time_entries_dao.get_all_old(conditions=conditions) @ns.doc('create_time_entry') @ns.expect(time_entry_input) diff --git a/time_tracker_api/time_entries/time_entries_query_builder.py b/time_tracker_api/time_entries/time_entries_query_builder.py index 40be129e..03bec701 100644 --- a/time_tracker_api/time_entries/time_entries_query_builder.py +++ b/time_tracker_api/time_entries/time_entries_query_builder.py @@ -7,7 +7,8 @@ def __init__(self): def add_sql_date_range_condition(self, date_range: tuple = None): if date_range and len(date_range) == 2: - start_date, end_date = date_range + start_date = date_range['start_date'] + end_date = date_range['end_date'] condition = """ ((c.start_date BETWEEN @start_date AND @end_date) OR (c.end_date BETWEEN @start_date AND @end_date)) diff --git a/time_tracker_api/time_entries/time_entries_repository.py b/time_tracker_api/time_entries/time_entries_repository.py index 7d0837e3..6223ff95 100644 --- a/time_tracker_api/time_entries/time_entries_repository.py +++ b/time_tracker_api/time_entries/time_entries_repository.py @@ -141,7 +141,37 @@ def count( return result.next() - def find_all( + def add_complementary_info(self, time_entries=None, max_count=None): + if time_entries: + custom_conditions = create_in_condition(time_entries, "project_id") + custom_conditions_activity = create_in_condition( + time_entries, "activity_id" + ) + + project_dao = projects_model.create_dao() + projects = project_dao.get_all( + custom_sql_conditions=[custom_conditions], + visible_only=False, + max_count=max_count, + ) + + add_project_info_to_time_entries(time_entries, projects) + + activity_dao = activities_model.create_dao() + activities = activity_dao.get_all( + custom_sql_conditions=[custom_conditions_activity], + visible_only=False, + max_count=max_count, + ) + add_activity_name_to_time_entries(time_entries, activities) + + users = AzureConnection().users() + add_user_email_to_time_entries(time_entries, users) + elif not time_entries and len(conditions) > 1: + abort(HTTPStatus.NOT_FOUND, "Time entry not found") + return time_entries + + def find_all_old( self, event_context: EventContext, conditions: dict = None, @@ -199,41 +229,71 @@ def find_all( abort(HTTPStatus.NOT_FOUND, "Time entry not found") return time_entries - def find_all_v2( + def find_all( self, + conditions, + custom_sql_conditions, event_context: EventContext, - time_entry_ids: List[str], - owner_ids: List[str], - date_range: tuple = None, - fields: dict = None, - limit: int = None, - offset: int = 0, + date_range: dict = None, + test_user_ids=None, + offset=0, + max_count=None, visible_only=True, + custom_params: dict = None, mapper: Callable = None, ): - limit = self.get_page_size_or(limit) partition_key_value = self.find_partition_key_value(event_context) - query_builder = ( - TimeEntryQueryBuilder() - .add_sql_date_range_condition(date_range) - .add_sql_in_condition(time_entry_ids) - .add_sql_in_condition(owner_ids) - .add_sql_where_equal_condition(fields) - .add_sql_limit_condition(limit) - .add_sql_offset_condition(offset) - .build() - ) + max_count = self.get_page_size_or(max_count) - query_str = query_builder.get_query() - params = query_builder.get_parameters() + params = [ + {"name": "@partition_key_value", "value": partition_key_value}, + {"name": "@offset", "value": offset}, + {"name": "@max_count", "value": max_count}, + ] + + params.extend(self.generate_params(conditions)) + params.extend(custom_params) + + query_str = """ + SELECT * FROM c + WHERE c.{partition_key_attribute}=@partition_key_value + {conditions_clause} + {visibility_condition} + {test_users_exclusion_condition} + {custom_sql_conditions_clause} + OFFSET @offset LIMIT @max_count + """.format( + partition_key_attribute=self.partition_key_attribute, + visibility_condition=self.create_sql_condition_for_visibility( + visible_only + ), + conditions_clause=self.create_sql_where_conditions(conditions), + test_users_exclusion_condition=self.create_sql_test_users_exclusion_condition( + test_user_ids + ), + custom_sql_conditions_clause=self.create_custom_sql_conditions( + custom_sql_conditions + ), + ) result = self.container.query_items( query=query_str, parameters=params, partition_key=partition_key_value, + max_item_count=max_count, ) + function_mapper = self.get_mapper_or_dict(mapper) - return list(map(function_mapper, result)) + time_entries = list(map(function_mapper, result)) + + return self.add_complementary_info(time_entries, max_count) + + def create_sql_test_users_exclusion_condition(self, test_user_ids): + if test_user_ids != None: + return ( + " AND c.owner_id NOT IN ('" + "','".join(test_user_ids) + "') " + ) + return "" def get_last_entry( self, From c1879abe708cda2b1aa005c4ba84951395765188 Mon Sep 17 00:00:00 2001 From: Pablo Date: Wed, 28 Apr 2021 14:15:21 -0500 Subject: [PATCH 2/2] fix: TT-219 automatic code formating --- .../time_entries/time_entries_model_test.py | 5 +- .../time_entries_namespace_test.py | 93 ++++++++++++++++++- .../time_entries/time_entries_repository.py | 17 ++-- 3 files changed, 105 insertions(+), 10 deletions(-) diff --git a/tests/time_tracker_api/time_entries/time_entries_model_test.py b/tests/time_tracker_api/time_entries/time_entries_model_test.py index be74746d..ccea2854 100644 --- a/tests/time_tracker_api/time_entries/time_entries_model_test.py +++ b/tests/time_tracker_api/time_entries/time_entries_model_test.py @@ -324,7 +324,6 @@ def test_find_all_v2( time_entry_repository.container = Mock() time_entry_repository.container.query_items = query_items_mock - time_entry_repository.add_complementary_info = Mock() time_entry_repository.add_complementary_info = query_items_mock result = time_entry_repository.find_all( @@ -371,7 +370,9 @@ def test_get_last_entry( time_entry_repository.container = Mock() time_entry_repository.container.query_items = query_items_mock - time_entry = time_entry_repository.get_last_entry('id1', ['id1'], event_context) + time_entry = time_entry_repository.get_last_entry( + 'id1', ['id1'], event_context + ) find_partition_key_value_mock.assert_called_once() assert isinstance(time_entry, TimeEntryCosmosDBModel) diff --git a/tests/time_tracker_api/time_entries/time_entries_namespace_test.py b/tests/time_tracker_api/time_entries/time_entries_namespace_test.py index 0da2964c..96d929bc 100644 --- a/tests/time_tracker_api/time_entries/time_entries_namespace_test.py +++ b/tests/time_tracker_api/time_entries/time_entries_namespace_test.py @@ -230,6 +230,95 @@ def test_get_time_entry_should_succeed_with_valid_id( 'current_user_is_tester, expected_user_ids', [ (True, ['id1', 'id1']), + ], +) +@patch( + 'commons.feature_toggles.feature_toggle_manager.FeatureToggleManager.get_azure_app_configuration_client' +) +@patch( + 'commons.feature_toggles.feature_toggle_manager.FeatureToggleManager.is_toggle_enabled_for_user' +) +def test_get_time_entries_by_type_of_user_when_is_user_tester( + is_toggle_enabled_for_user_mock, + get_azure_app_configuration_client_mock, + get_test_user_ids_mock, + is_test_user_mock, + client: FlaskClient, + valid_header: dict, + time_entries_dao, + current_user_is_tester, + expected_user_ids, +): + is_toggle_enabled_for_user_mock.return_value = True + test_user_id = "id1" + non_test_user_id = "id2" + te1 = TimeEntryCosmosDBModel( + { + "id": '1', + "project_id": "1", + "owner_id": test_user_id, + "tenant_id": '1', + "start_date": "", + } + ) + te2 = TimeEntryCosmosDBModel( + { + "id": '2', + "project_id": "2", + "owner_id": test_user_id, + "tenant_id": '2', + "start_date": "", + } + ) + + find_all_mock = Mock() + find_all_mock.return_value = [te1, te2] + + time_entries_dao.repository.find_all = find_all_mock + + is_test_user_mock.return_value = current_user_is_tester + + response = client.get( + "/time-entries?user_id=*", headers=valid_header, follow_redirects=True + ) + + get_test_user_ids_mock.assert_not_called() + find_all_mock.assert_called() + + expected_user_ids_in_time_entries = expected_user_ids + actual_user_ids_in_time_entries = [ + time_entry["owner_id"] for time_entry in json.loads(response.data) + ] + assert expected_user_ids_in_time_entries == actual_user_ids_in_time_entries + + +@patch( + 'time_tracker_api.time_entries.time_entries_dao.TimeEntriesCosmosDBDao.create_event_context', + Mock(), +) +@patch( + 'time_tracker_api.time_entries.time_entries_dao.TimeEntriesCosmosDBDao.build_custom_query', + Mock(), +) +@patch( + 'time_tracker_api.time_entries.time_entries_dao.TimeEntriesCosmosDBDao.handle_date_filter_args', + Mock(), +) +@patch( + 'time_tracker_api.time_entries.time_entries_repository.TimeEntryCosmosDBRepository.create_sql_date_range_filter', + Mock(), +) +@patch( + 'commons.data_access_layer.cosmos_db.CosmosDBRepository.generate_params', + Mock(), +) +@patch('msal.ConfidentialClientApplication', Mock()) +@patch('utils.azure_users.AzureConnection.get_token', Mock()) +@patch('utils.azure_users.AzureConnection.is_test_user') +@patch('utils.azure_users.AzureConnection.get_test_user_ids') +@pytest.mark.parametrize( + 'current_user_is_tester, expected_user_ids', + [ (False, ['id1', 'id1']), ], ) @@ -239,7 +328,7 @@ def test_get_time_entry_should_succeed_with_valid_id( @patch( 'commons.feature_toggles.feature_toggle_manager.FeatureToggleManager.is_toggle_enabled_for_user' ) -def test_get_time_entries_by_type_of_user( +def test_get_time_entries_by_type_of_user_when_is_not_user_tester( is_toggle_enabled_for_user_mock, get_azure_app_configuration_client_mock, get_test_user_ids_mock, @@ -284,7 +373,7 @@ def test_get_time_entries_by_type_of_user( "/time-entries?user_id=*", headers=valid_header, follow_redirects=True ) - is_test_user_mock.assert_called() + get_test_user_ids_mock.assert_called() find_all_mock.assert_called() expected_user_ids_in_time_entries = expected_user_ids diff --git a/time_tracker_api/time_entries/time_entries_repository.py b/time_tracker_api/time_entries/time_entries_repository.py index 6223ff95..83d74086 100644 --- a/time_tracker_api/time_entries/time_entries_repository.py +++ b/time_tracker_api/time_entries/time_entries_repository.py @@ -9,6 +9,7 @@ ) from utils.time import current_datetime_str +from utils.repository import convert_list_to_tuple_string from utils.extend_model import ( add_project_info_to_time_entries, @@ -290,9 +291,9 @@ def find_all( def create_sql_test_users_exclusion_condition(self, test_user_ids): if test_user_ids != None: - return ( - " AND c.owner_id NOT IN ('" + "','".join(test_user_ids) + "') " - ) + tuple_string = convert_list_to_tuple_string(test_user_ids) + return "AND c.owner_id NOT IN {list}".format(list=tuple_string) + return "" def get_last_entry( @@ -321,12 +322,16 @@ def get_last_entry( function_mapper = self.get_mapper_or_dict(mapper) return function_mapper(next(result)) - def update_last_entry( - self, owner_id: str, start_date: str, id_running_entry: str, event_context: EventContext + self, + owner_id: str, + start_date: str, + id_running_entry: str, + event_context: EventContext, ): last_entry = self.get_last_entry( - owner_id, id_running_entry, event_context) + owner_id, id_running_entry, event_context + ) end_date = str_to_datetime(last_entry.end_date) _start_date = str_to_datetime(start_date)