Skip to content

Commit 78ed111

Browse files
authored
Merge pull request #163 from ioet/feature/filter-time-entries-by-uuid
closes #163
2 parents 7659c9f + 0181ca9 commit 78ed111

File tree

3 files changed

+95
-20
lines changed

3 files changed

+95
-20
lines changed

time_tracker_api/time_entries/time_entries_model.py

Lines changed: 39 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
import abc
22
from dataclasses import dataclass, field
33
from typing import List, Callable
4-
54
from azure.cosmos import PartitionKey
65
from azure.cosmos.exceptions import CosmosResourceNotFoundError
7-
6+
from flask_restplus import abort
87
from flask_restplus._http import HTTPStatus
98

109
from commons.data_access_layer.cosmos_db import (
@@ -22,10 +21,16 @@
2221
from commons.data_access_layer.database import EventContext
2322
from time_tracker_api.activities import activities_model
2423

25-
from utils.extend_model import add_project_name_to_time_entries, add_activity_name_to_time_entries
24+
from utils.extend_model import (
25+
add_project_name_to_time_entries,
26+
add_activity_name_to_time_entries,
27+
)
2628
from utils import worked_time
2729
from utils.worked_time import str_to_datetime
28-
30+
from utils.extend_model import (
31+
create_in_condition,
32+
create_custom_query_from_str,
33+
)
2934
from time_tracker_api.projects.projects_model import ProjectCosmosDBModel
3035
from time_tracker_api.projects import projects_model
3136
from time_tracker_api.database import CRUDDao, APICosmosDBDao
@@ -144,13 +149,12 @@ def find_all(
144149
self,
145150
event_context: EventContext,
146151
conditions: dict = {},
152+
custom_sql_conditions: List[str] = [],
147153
date_range: dict = {},
148154
):
149-
custom_sql_conditions = [self.create_sql_date_range_filter(date_range)]
150-
151-
if event_context.is_admin:
152-
conditions.pop("owner_id")
153-
# TODO should be removed when implementing a role-based permission module ↑
155+
custom_sql_conditions.append(
156+
self.create_sql_date_range_filter(date_range)
157+
)
154158

155159
custom_params = self.generate_params(date_range)
156160
time_entries = CosmosDBRepository.find_all(
@@ -162,14 +166,8 @@ def find_all(
162166
)
163167

164168
if time_entries:
165-
projects_id = [str(project.project_id) for project in time_entries]
166-
p_ids = (
167-
str(tuple(projects_id)).replace(",", "")
168-
if len(projects_id) == 1
169-
else str(tuple(projects_id))
170-
)
171-
custom_conditions = "c.id IN {}".format(p_ids)
172-
# TODO this must be refactored to be used from the utils module ↑
169+
custom_conditions = create_in_condition(time_entries, "project_id")
170+
173171
project_dao = projects_model.create_dao()
174172
projects = project_dao.get_all(
175173
custom_sql_conditions=[custom_conditions]
@@ -343,10 +341,30 @@ def stop_time_entry_if_was_left_running(
343341
def get_all(self, conditions: dict = None, **kwargs) -> list:
344342
event_ctx = self.create_event_context("read-many")
345343
conditions.update({"owner_id": event_ctx.user_id})
346-
344+
custom_query = []
345+
if "user_id" in conditions:
346+
if event_ctx.is_admin:
347+
conditions.pop("owner_id")
348+
custom_query = (
349+
[]
350+
if conditions.get("user_id") == "*"
351+
else [
352+
create_custom_query_from_str(
353+
conditions.get("user_id"), "c.owner_id"
354+
)
355+
]
356+
)
357+
conditions.pop("user_id")
358+
else:
359+
abort(
360+
HTTPStatus.FORBIDDEN, "You don't have enough permissions."
361+
)
347362
date_range = self.handle_date_filter_args(args=conditions)
348363
return self.repository.find_all(
349-
event_ctx, conditions=conditions, date_range=date_range
364+
event_ctx,
365+
conditions=conditions,
366+
custom_sql_conditions=custom_query,
367+
date_range=date_range,
350368
)
351369

352370
def get(self, id):
@@ -432,6 +450,8 @@ def get_worked_time(self, conditions: dict = {}):
432450
@staticmethod
433451
def handle_date_filter_args(args: dict) -> dict:
434452
date_range = None
453+
year = None
454+
month = None
435455
if 'month' and 'year' in args:
436456
month = int(args.get("month"))
437457
year = int(args.get("year"))

time_tracker_api/time_entries/time_entries_namespace.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,13 +143,22 @@
143143
)
144144

145145
# custom attributes filter
146+
attributes_filter.add_argument(
147+
'user_id',
148+
required=False,
149+
store_missing=False,
150+
help="(Filter) User to filter by",
151+
location='args',
152+
)
153+
146154
attributes_filter.add_argument(
147155
'month',
148156
required=False,
149157
store_missing=False,
150158
help="(Filter) Month to filter by",
151159
location='args',
152160
)
161+
153162
attributes_filter.add_argument(
154163
'year',
155164
required=False,
@@ -165,6 +174,7 @@
165174
help="(Filter) Start to filter by",
166175
location='args',
167176
)
177+
168178
attributes_filter.add_argument(
169179
'end_date',
170180
required=False,

utils/extend_model.py

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
import re
2+
3+
14
def add_customer_name_to_projects(projects, customers):
25
"""
36
Add attribute customer_name in project model, based on customer_id of the
@@ -32,4 +35,46 @@ def add_activity_name_to_time_entries(time_entries, activities):
3235
for time_entry in time_entries:
3336
for activity in activities:
3437
if time_entry.activity_id == activity.id:
35-
setattr(time_entry, 'activity_name', activity.name)
38+
setattr(time_entry, 'activity_name', activity.name)
39+
40+
41+
def create_in_condition(
42+
data_object: list, attr_to_filter: str = "", first_attr: str = "c.id"
43+
):
44+
"""
45+
Function to create a custom query string from a list of objects or a list of strings.
46+
:param data_object: List of objects or a list of strings
47+
:param attr_to_filter: Attribute to retrieve the value of the objects (Only in case it is a list of objects)
48+
:param first_attr: First attribute to build the condition
49+
:return: Custom condition string
50+
"""
51+
attr_filter = re.sub('[^a-zA-Z_$0-9]', '', attr_to_filter)
52+
object_id = (
53+
[str(i) for i in data_object]
54+
if type(data_object[0]) == str
55+
else [str(eval(f"object.{attr_filter}")) for object in data_object]
56+
)
57+
ids = (
58+
str(tuple(object_id)).replace(",", "")
59+
if len(object_id) == 1
60+
else str(tuple(object_id))
61+
)
62+
return "{} IN {}".format(first_attr, ids)
63+
64+
65+
def create_custom_query_from_str(
66+
data: str, first_attr, delimiter: str = ","
67+
) -> str:
68+
"""
69+
Function to create a string condition for url parameters (Example: data?values=value1,value2 or data?values=*)
70+
:param data: String to build the query
71+
:param first_attr: First attribute to build the condition
72+
:param delimiter: String delimiter
73+
:return: Custom condition string
74+
"""
75+
data = data.split(delimiter)
76+
if len(data) > 1:
77+
query_str = create_in_condition(data, first_attr=first_attr)
78+
else:
79+
query_str = "{} = '{}'".format(first_attr, data[0])
80+
return query_str

0 commit comments

Comments
 (0)