Skip to content

Commit d100863

Browse files
committed
fix: ♻️ make method elapsed time of class TimeEntryModel
1 parent d1413ee commit d100863

File tree

5 files changed

+105
-95
lines changed

5 files changed

+105
-95
lines changed

requirements/time_tracker_api/prod.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,7 @@ PyJWT==1.7.1
3737

3838
#Azure
3939
msal==1.3.0
40+
41+
# Time utils
42+
pytz==2019.3
43+
python-dateutil==2.8.1

time_tracker_api/time_entries/time_entries_model.py

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
from flask_restplus import abort
77
from flask_restplus._http import HTTPStatus
88

9+
from datetime import datetime, timedelta
10+
911
from commons.data_access_layer.cosmos_db import (
1012
CosmosDBDao,
1113
CosmosDBRepository,
@@ -25,14 +27,14 @@
2527
)
2628
from utils.time import (
2729
datetime_str,
30+
str_to_datetime,
2831
get_current_year,
2932
get_current_month,
3033
get_current_day,
3134
get_date_range_of_month,
3235
current_datetime_str,
3336
)
3437
from utils import worked_time
35-
from utils.worked_time import str_to_datetime
3638
from utils.azure_users import AzureConnection
3739

3840
from time_tracker_api.projects.projects_model import ProjectCosmosDBModel
@@ -105,6 +107,18 @@ def start_date_at_midnight(self) -> str:
105107
start_date.replace(hour=23, minute=59, second=59, microsecond=0)
106108
)
107109

110+
@property
111+
def elapsed_time(self) -> timedelta:
112+
start_datetime = str_to_datetime(self.start_date)
113+
end_datetime = str_to_datetime(self.end_date)
114+
return end_datetime - start_datetime
115+
116+
def in_range(self, start_date: datetime, end_date: datetime) -> bool:
117+
return (
118+
start_date <= str_to_datetime(self.start_date) <= end_date
119+
and start_date <= str_to_datetime(self.end_date) <= end_date
120+
)
121+
108122
def __add__(self, other):
109123
if type(other) is ProjectCosmosDBModel:
110124
time_entry = self.__class__
@@ -114,7 +128,7 @@ def __add__(self, other):
114128
raise NotImplementedError
115129

116130
def __repr__(self):
117-
return '<Time Entry %r>' % self.start_date # pragma: no cover
131+
return f'<Time Entry {self.start_date} - {self.end_date}>' # pragma: no cover
118132

119133
def __str___(self):
120134
return (
@@ -171,17 +185,21 @@ def find_all(
171185

172186
if time_entries:
173187
custom_conditions = create_in_condition(time_entries, "project_id")
174-
custom_conditions_activity = create_in_condition(time_entries, "activity_id")
188+
custom_conditions_activity = create_in_condition(
189+
time_entries, "activity_id"
190+
)
175191

176192
project_dao = projects_model.create_dao()
177193
projects = project_dao.get_all(
178-
custom_sql_conditions=[custom_conditions],
179-
visible_only=False
194+
custom_sql_conditions=[custom_conditions], visible_only=False
180195
)
181196
add_project_name_to_time_entries(time_entries, projects)
182197

183198
activity_dao = activities_model.create_dao()
184-
activities = activity_dao.get_all(custom_sql_conditions=[custom_conditions_activity], visible_only=False)
199+
activities = activity_dao.get_all(
200+
custom_sql_conditions=[custom_conditions_activity],
201+
visible_only=False,
202+
)
185203
add_activity_name_to_time_entries(time_entries, activities)
186204

187205
users = AzureConnection().users()

time_tracker_api/time_entries/time_entries_namespace.py

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,6 @@
3333
description='The id of the selected project',
3434
example=faker.uuid4(),
3535
),
36-
'start_date': fields.DateTime(
37-
dt_format='iso8601',
38-
title='Start date',
39-
required=False,
40-
description='When the user started doing this activity',
41-
example=datetime_str(current_datetime() - timedelta(days=1)),
42-
),
4336
'activity_id': UUID(
4437
title='Activity',
4538
required=False,
@@ -53,8 +46,13 @@
5346
example=faker.paragraph(nb_sentences=2),
5447
max_length=COMMENTS_MAX_LENGTH,
5548
),
56-
'end_date': fields.DateTime(
57-
dt_format='iso8601',
49+
'start_date': fields.String(
50+
title='Start date',
51+
required=False,
52+
description='When the user started doing this activity',
53+
example=datetime_str(current_datetime() - timedelta(days=1)),
54+
),
55+
'end_date': fields.String(
5856
title='End date',
5957
required=False,
6058
description='When the user ended doing this activity',

utils/time.py

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1+
import pytz
12
from typing import Dict
2-
from datetime import datetime, timezone
3+
from datetime import datetime, timezone, timedelta
34

45

56
def current_datetime_str() -> str:
@@ -9,7 +10,7 @@ def current_datetime_str() -> str:
910

1011

1112
def current_datetime() -> datetime:
12-
return datetime.now(timezone.utc)
13+
return datetime.now(pytz.UTC)
1314

1415

1516
def get_current_year() -> int:
@@ -58,3 +59,37 @@ def get_last_day_of_month(year: int, month: int) -> int:
5859
'start_date': datetime_str(start_date),
5960
'end_date': datetime_str(end_date),
6061
}
62+
63+
64+
def start_datetime_of_current_month() -> datetime:
65+
return datetime(
66+
year=get_current_year(),
67+
month=get_current_month(),
68+
day=1,
69+
tzinfo=timezone.utc,
70+
)
71+
72+
73+
def start_datetime_of_current_week() -> datetime:
74+
today = current_datetime()
75+
monday = today - timedelta(days=today.weekday())
76+
monday = monday.replace(hour=0, minute=0, second=0, microsecond=000000)
77+
return monday
78+
79+
80+
def start_datetime_of_current_day() -> datetime:
81+
today = current_datetime()
82+
today = today.replace(hour=0, minute=0, second=0, microsecond=000000)
83+
return today
84+
85+
86+
def start_datetime_of_current_month_str() -> str:
87+
return datetime_str(start_datetime_of_current_month())
88+
89+
90+
def str_to_datetime(value: str) -> datetime:
91+
from dateutil.parser import isoparse
92+
from dateutil.tz import tzutc
93+
94+
assert type(isoparse(value).tzinfo) == tzutc
95+
return isoparse(value)

utils/worked_time.py

Lines changed: 33 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -1,68 +1,21 @@
1-
from datetime import datetime, timedelta, timezone
1+
from datetime import datetime, timedelta
22
from utils.time import (
3-
datetime_str,
4-
get_current_month,
5-
get_current_year,
63
current_datetime,
74
current_datetime_str,
5+
start_datetime_of_current_month,
6+
start_datetime_of_current_week,
7+
start_datetime_of_current_day,
8+
start_datetime_of_current_month_str,
89
)
910

1011

11-
def start_datetime_of_current_month() -> datetime:
12-
return datetime(
13-
year=get_current_year(),
14-
month=get_current_month(),
15-
day=1,
16-
tzinfo=timezone.utc,
17-
)
18-
19-
20-
def start_datetime_of_current_week() -> datetime:
21-
today = current_datetime()
22-
monday = today - timedelta(days=today.weekday())
23-
monday = monday.replace(hour=0, minute=0, second=0, microsecond=000000)
24-
return monday
25-
26-
27-
def start_datetime_of_current_day() -> datetime:
28-
today = current_datetime()
29-
today = today.replace(hour=0, minute=0, second=0, microsecond=000000)
30-
return today
31-
32-
33-
def start_datetime_of_current_month_str() -> str:
34-
return datetime_str(start_datetime_of_current_month())
35-
36-
37-
def str_to_datetime(
38-
value: str, conversion_format: str = '%Y-%m-%dT%H:%M:%S.%fZ'
39-
) -> datetime:
40-
if 'Z' in value:
41-
return datetime.strptime(value, conversion_format).astimezone(
42-
timezone.utc
43-
)
44-
else:
45-
return datetime.fromisoformat(value).astimezone(timezone.utc)
46-
47-
4812
def date_range():
4913
return {
5014
"start_date": start_datetime_of_current_month_str(),
5115
"end_date": current_datetime_str(),
5216
}
5317

5418

55-
def filter_time_entries(
56-
time_entries, start_date: datetime, end_date: datetime = current_datetime()
57-
):
58-
return [
59-
t
60-
for t in time_entries
61-
if start_date <= str_to_datetime(t.start_date) <= end_date
62-
or start_date <= str_to_datetime(t.end_date) <= end_date
63-
]
64-
65-
6619
def stop_running_time_entry(time_entries):
6720
for t in time_entries:
6821
if t.end_date is None:
@@ -73,20 +26,18 @@ class WorkedTime:
7326
def __init__(self, time_entries):
7427
self.time_entries = time_entries
7528

76-
def total_time_in_seconds(self):
77-
times = []
78-
79-
for t in self.time_entries:
80-
start_datetime = str_to_datetime(t.start_date)
81-
end_datetime = str_to_datetime(t.end_date)
82-
83-
elapsed_time = end_datetime - start_datetime
84-
times.append(elapsed_time)
85-
86-
total_time = timedelta()
87-
for time in times:
88-
total_time += time
29+
@classmethod
30+
def from_time_entries_in_range(
31+
cls, time_entries, start_date: datetime, end_date: datetime
32+
):
33+
time_entries_in_range = [
34+
t for t in time_entries if t.in_range(start_date, end_date)
35+
]
36+
return cls(time_entries_in_range)
8937

38+
def total_time_in_seconds(self):
39+
times = [t.elapsed_time for t in self.time_entries]
40+
total_time = sum(times, timedelta())
9041
return total_time.total_seconds()
9142

9243
def hours(self):
@@ -107,28 +58,32 @@ def summary(self):
10758

10859

10960
def worked_time_in_day(time_entries):
110-
day_time_entries = filter_time_entries(
111-
time_entries, start_date=start_datetime_of_current_day()
112-
)
113-
return WorkedTime(day_time_entries).summary()
61+
return WorkedTime.from_time_entries_in_range(
62+
time_entries,
63+
start_date=start_datetime_of_current_day(),
64+
end_date=current_datetime(),
65+
).summary()
11466

11567

11668
def worked_time_in_week(time_entries):
117-
week_time_entries = filter_time_entries(
118-
time_entries, start_date=start_datetime_of_current_week()
119-
)
120-
return WorkedTime(week_time_entries).summary()
69+
return WorkedTime.from_time_entries_in_range(
70+
time_entries,
71+
start_date=start_datetime_of_current_week(),
72+
end_date=current_datetime(),
73+
).summary()
12174

12275

12376
def worked_time_in_month(time_entries):
124-
month_time_entries = filter_time_entries(
125-
time_entries, start_date=start_datetime_of_current_month()
126-
)
127-
return WorkedTime(month_time_entries).summary()
77+
return WorkedTime.from_time_entries_in_range(
78+
time_entries,
79+
start_date=start_datetime_of_current_month(),
80+
end_date=current_datetime(),
81+
).summary()
12882

12983

13084
def summary(time_entries, time_offset):
131-
print(time_offset)
85+
offset_in_minutes = time_offset if time_offset else 300
86+
print(offset_in_minutes)
13287
stop_running_time_entry(time_entries)
13388
return {
13489
'day': worked_time_in_day(time_entries),

0 commit comments

Comments
 (0)