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
58 changes: 39 additions & 19 deletions time_tracker_api/time_entries/time_entries_model.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import abc
from dataclasses import dataclass, field
from typing import List, Callable

from azure.cosmos import PartitionKey
from azure.cosmos.exceptions import CosmosResourceNotFoundError

from flask_restplus import abort
from flask_restplus._http import HTTPStatus

from commons.data_access_layer.cosmos_db import (
Expand All @@ -22,10 +21,16 @@
from commons.data_access_layer.database import EventContext
from time_tracker_api.activities import activities_model

from utils.extend_model import add_project_name_to_time_entries, add_activity_name_to_time_entries
from utils.extend_model import (
add_project_name_to_time_entries,
add_activity_name_to_time_entries,
)
from utils import worked_time
from utils.worked_time import str_to_datetime

from utils.extend_model import (
create_in_condition,
create_custom_query_from_str,
)
from time_tracker_api.projects.projects_model import ProjectCosmosDBModel
from time_tracker_api.projects import projects_model
from time_tracker_api.database import CRUDDao, APICosmosDBDao
Expand Down Expand Up @@ -144,13 +149,12 @@ def find_all(
self,
event_context: EventContext,
conditions: dict = {},
custom_sql_conditions: List[str] = [],
date_range: dict = {},
):
custom_sql_conditions = [self.create_sql_date_range_filter(date_range)]

if event_context.is_admin:
conditions.pop("owner_id")
# TODO should be removed when implementing a role-based permission module ↑
custom_sql_conditions.append(
self.create_sql_date_range_filter(date_range)
)

custom_params = self.generate_params(date_range)
time_entries = CosmosDBRepository.find_all(
Expand All @@ -162,14 +166,8 @@ def find_all(
)

if time_entries:
projects_id = [str(project.project_id) for project in time_entries]
p_ids = (
str(tuple(projects_id)).replace(",", "")
if len(projects_id) == 1
else str(tuple(projects_id))
)
custom_conditions = "c.id IN {}".format(p_ids)
# TODO this must be refactored to be used from the utils module ↑
custom_conditions = create_in_condition(time_entries, "project_id")

project_dao = projects_model.create_dao()
projects = project_dao.get_all(
custom_sql_conditions=[custom_conditions]
Expand Down Expand Up @@ -343,10 +341,30 @@ def stop_time_entry_if_was_left_running(
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})

custom_query = []
if "user_id" in conditions:
if event_ctx.is_admin:
conditions.pop("owner_id")
custom_query = (
[]
if conditions.get("user_id") == "*"
else [
create_custom_query_from_str(
conditions.get("user_id"), "c.owner_id"
)
]
)
conditions.pop("user_id")
else:
abort(
HTTPStatus.FORBIDDEN, "You don't have enough permissions."
)
date_range = self.handle_date_filter_args(args=conditions)
return self.repository.find_all(
event_ctx, conditions=conditions, date_range=date_range
event_ctx,
conditions=conditions,
custom_sql_conditions=custom_query,
date_range=date_range,
)

def get(self, id):
Expand Down Expand Up @@ -432,6 +450,8 @@ def get_worked_time(self, conditions: dict = {}):
@staticmethod
def handle_date_filter_args(args: dict) -> dict:
date_range = None
year = None
month = None
if 'month' and 'year' in args:
month = int(args.get("month"))
year = int(args.get("year"))
Expand Down
10 changes: 10 additions & 0 deletions time_tracker_api/time_entries/time_entries_namespace.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,13 +143,22 @@
)

# custom attributes filter
attributes_filter.add_argument(
'user_id',
required=False,
store_missing=False,
help="(Filter) User to filter by",
location='args',
)

attributes_filter.add_argument(
'month',
required=False,
store_missing=False,
help="(Filter) Month to filter by",
location='args',
)

attributes_filter.add_argument(
'year',
required=False,
Expand All @@ -165,6 +174,7 @@
help="(Filter) Start to filter by",
location='args',
)

attributes_filter.add_argument(
'end_date',
required=False,
Expand Down
47 changes: 46 additions & 1 deletion utils/extend_model.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import re


def add_customer_name_to_projects(projects, customers):
"""
Add attribute customer_name in project model, based on customer_id of the
Expand Down Expand Up @@ -32,4 +35,46 @@ def add_activity_name_to_time_entries(time_entries, activities):
for time_entry in time_entries:
for activity in activities:
if time_entry.activity_id == activity.id:
setattr(time_entry, 'activity_name', activity.name)
setattr(time_entry, 'activity_name', activity.name)


def create_in_condition(
data_object: list, attr_to_filter: str = "", first_attr: str = "c.id"
):
"""
Function to create a custom query string from a list of objects or a list of strings.
:param data_object: List of objects or a list of strings
:param attr_to_filter: Attribute to retrieve the value of the objects (Only in case it is a list of objects)
:param first_attr: First attribute to build the condition
:return: Custom condition string
"""
attr_filter = re.sub('[^a-zA-Z_$0-9]', '', attr_to_filter)
object_id = (
[str(i) for i in data_object]
if type(data_object[0]) == str
else [str(eval(f"object.{attr_filter}")) for object in data_object]
)
ids = (
str(tuple(object_id)).replace(",", "")
if len(object_id) == 1
else str(tuple(object_id))
)
return "{} IN {}".format(first_attr, ids)


def create_custom_query_from_str(
data: str, first_attr, delimiter: str = ","
) -> str:
"""
Function to create a string condition for url parameters (Example: data?values=value1,value2 or data?values=*)
:param data: String to build the query
:param first_attr: First attribute to build the condition
:param delimiter: String delimiter
:return: Custom condition string
"""
data = data.split(delimiter)
if len(data) > 1:
query_str = create_in_condition(data, first_attr=first_attr)
else:
query_str = "{} = '{}'".format(first_attr, data[0])
return query_str