Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ class Project(NamedTuple):
project_type_id: int
customer_id: str
tenant_id: str
status: str


class ProjectFactory(Factory):
Expand All @@ -28,3 +29,4 @@ def __init__(self, project_type_id, customer_id):
name = Faker('name')
description = Faker('sentence', nb_words=10)
tenant_id = get_time_tracker_tenant_id()
status = 'active'
1 change: 1 addition & 0 deletions cosmosdb_emulator/time_tracker_cli/utils/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,6 @@ def get_project_json(project_factory: ProjectFactory) -> dict:
'customer_id': project_factory.customer_id,
'project_type_id': project_factory.project_type_id,
'tenant_id': project_factory.tenant_id,
'status': project_factory.status,
}
return project
61 changes: 61 additions & 0 deletions tests/time_tracker_api/projects/projects_model_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,18 @@
ProjectCosmosDBRepository,
ProjectCosmosDBModel,
create_dao,
ProjectCosmosDBDao,
)
from faker import Faker

from time_tracker_api.time_entries.time_entries_dao import (
TimeEntriesCosmosDBDao,
)
from time_tracker_api.time_entries.time_entries_model import (
TimeEntryCosmosDBModel,
)
from utils.enums.status import Status

fake = Faker()


Expand Down Expand Up @@ -138,3 +147,55 @@ def test_get_all_projects_with_customers(
assert isinstance(projects[0], ProjectCosmosDBModel)
assert projects[0].__dict__['customer_name'] == customer_data['name']
assert len(projects) == 1


def test_get_recent_projects_get_all_method_should_have_been_called_with_specific_arguments(
mocker,
):
projects_amount = 5
expected_conditions = {'status': Status.ACTIVE.value}
expected_projects_ids = list(
set([fake.uuid4() for i in range(projects_amount)])
)
user_time_entries = []

for project_id in expected_projects_ids:
current_entry = TimeEntryCosmosDBModel(
{'project_id': project_id, 'id': fake.uuid4()}
)
user_time_entries.append(current_entry)

mocker.patch.object(
TimeEntriesCosmosDBDao,
'get_latest_entries',
return_value=user_time_entries,
)
project_cosmos_db_dao_get_all_mock = mocker.patch.object(
ProjectCosmosDBDao, 'get_all'
)
projects_dao = create_dao()

projects_dao.get_recent_projects()

project_cosmos_db_dao_get_all_mock.assert_called_once_with(
conditions=expected_conditions,
project_ids=expected_projects_ids,
customer_status=Status.ACTIVE.value,
)


def test_get_recent_projects_should_return_an_empty_array_if_the_user_has_no_entries(
mocker,
):
user_time_entries = []
mocker.patch.object(
TimeEntriesCosmosDBDao,
'get_latest_entries',
return_value=user_time_entries,
)

projects_dao = create_dao()

recent_projects = projects_dao.get_recent_projects()

assert len(recent_projects) == 0
17 changes: 17 additions & 0 deletions tests/time_tracker_api/projects/projects_namespace_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -309,3 +309,20 @@ def test_delete_project_should_return_unprocessable_entity_for_invalid_id_format
repository_remove_mock.assert_called_once_with(
str(invalid_id), {'status': Status.INACTIVE.value}, ANY
)


def test_get_recent_projects_should_call_method_get_recent_projects_from_project_dao(
client: FlaskClient, mocker: MockFixture, valid_header: dict
):
project_dao_get_recent_projects_mock = mocker.patch.object(
ProjectCosmosDBDao, 'get_recent_projects', return_value=[]
)

response = client.get(
"/projects/recent",
headers=valid_header,
follow_redirects=True,
)

assert response.status_code == HTTPStatus.OK
project_dao_get_recent_projects_mock.assert_called_once()
49 changes: 49 additions & 0 deletions tests/time_tracker_api/time_entries/time_entries_dao_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
from unittest.mock import ANY

from time_tracker_api.database import APICosmosDBDao
from time_tracker_api.time_entries.time_entries_repository import (
TimeEntryCosmosDBRepository,
)


def test_get_latest_entries_must_be_called_with_default_amount_of_entries(
mocker, time_entries_dao
):
expected_conditions = {'owner_id': ANY}

expected_entries_amount = 20

time_entries_repository_find_all_mock = mocker.patch.object(
TimeEntryCosmosDBRepository, 'find_all'
)
mocker.patch.object(APICosmosDBDao, 'create_event_context')

time_entries_dao.get_latest_entries()

time_entries_repository_find_all_mock.assert_called_with(
conditions=expected_conditions,
max_count=expected_entries_amount,
event_context=ANY,
)


def test_get_latest_entries_must_be_called_with_amount_of_entries_passed_in_condition(
mocker, time_entries_dao
):
time_entries_repository_find_all_mock = mocker.patch.object(
TimeEntryCosmosDBRepository, 'find_all'
)
mocker.patch.object(APICosmosDBDao, 'create_event_context')

expected_entries_amount = 40
conditions = {'limit': expected_entries_amount}

time_entries_dao.get_latest_entries(conditions=conditions)

conditions.update({'owner_id': ANY})

time_entries_repository_find_all_mock.assert_called_with(
conditions=conditions,
max_count=expected_entries_amount,
event_context=ANY,
)
32 changes: 31 additions & 1 deletion time_tracker_api/projects/projects_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
create_dao as project_types_create_dao,
)
from time_tracker_api.customers.customers_model import CustomerCosmosDBModel
from utils.enums.status import Status
from utils.query_builder import CosmosDBQueryBuilder
from utils.extend_model import (
add_customer_name_to_projects,
Expand Down Expand Up @@ -158,8 +159,37 @@ def get_all(
add_customer_name_to_projects(projects, customers)
return projects

def get_recent_projects(self):
"""
Gets the last projects in which the person has generated entries.
The import had to be carried out within the method to avoid circular dependency.
"""
from time_tracker_api.time_entries.time_entries_dao import (
create_dao as create_entries_dao,
)

recent_projects = []
time_entries_dao = create_entries_dao()
last_time_entries = time_entries_dao.get_latest_entries()

last_time_entries_amount = len(last_time_entries)

if last_time_entries_amount == 0:
return recent_projects

project_ids = list(
set([entry.project_id for entry in last_time_entries])
)
conditions = {'status': Status.ACTIVE.value}
recent_projects = self.get_all(
conditions=conditions,
project_ids=project_ids,
customer_status=Status.ACTIVE.value,
)

return recent_projects


def create_dao() -> ProjectDao:
repository = ProjectCosmosDBRepository()

return ProjectCosmosDBDao(repository)
8 changes: 8 additions & 0 deletions time_tracker_api/projects/projects_namespace.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,3 +193,11 @@ def delete(self, id):
"""Delete a project"""
project_dao.update(id, {'status': Status.INACTIVE.value})
return None, HTTPStatus.NO_CONTENT


@ns.route('/recent')
class RecentProjects(Resource):
@ns.doc('list_recent_projects')
@ns.marshal_list_with(project)
def get(self):
return project_dao.get_recent_projects()
23 changes: 21 additions & 2 deletions time_tracker_api/time_entries/time_entries_dao.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,10 @@
from commons.data_access_layer.cosmos_db import (
CosmosDBDao,
CustomError,
CosmosDBRepository,
)
from utils.extend_model import (
add_project_info_to_time_entries,
add_activity_name_to_time_entries,
create_custom_query_from_str,
create_list_from_str,
)
from utils.time import (
Expand Down Expand Up @@ -121,6 +119,27 @@ def get_all(self, conditions: dict = None, **kwargs) -> list:

return time_entries_list

def get_latest_entries(self, conditions: dict = None):
"""
Get the latest entries without taking into account a data range.
It would only be necessary to pass the number of last entries that
you need, this parameter must be passed by the conditions.
The default value for the entries amount is 20.
"""
conditions = conditions if conditions else {}

default_entries_amount = 20
event_context = self.create_event_context('read_many')
conditions.update({'owner_id': event_context.user_id})
entries_amount = conditions.pop("limit", default_entries_amount)
time_entries = self.repository.find_all(
conditions=conditions,
max_count=entries_amount,
event_context=event_context,
)

return time_entries

def get_lastest_entries_by_project(
self, conditions: dict = None, **kwargs
) -> list:
Expand Down