Skip to content

Commit 54eeb7e

Browse files
committed
feat: TT-403 deleted time entries
1 parent cbbb82d commit 54eeb7e

File tree

10 files changed

+180
-113
lines changed

10 files changed

+180
-113
lines changed

V2/pyvenv.cfg

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
home = /usr/bin
2+
include-system-site-packages = false
3+
version = 3.8.10

V2/tests/api/azure/time_entry_azure_endpoints_test.py

Lines changed: 75 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,86 @@
1-
from time_tracker.time_entries._application import _time_entries as time_entries
1+
import pytest
2+
import json
3+
from faker import Faker
24

35
import azure.functions as func
4-
import json
6+
7+
from time_tracker.time_entries._application import _time_entries as time_entries
8+
import time_tracker.time_entries._application._time_entries as azure_time_entries
9+
import time_tracker.time_entries._infrastructure as infrastructure
10+
from time_tracker._infrastructure import DB
11+
from time_tracker.time_entries import _domain
12+
513

614

715
TIME_ENTRY_URL = "/api/time-entries/"
816

917

18+
@pytest.fixture(name='insert_time_entry')
19+
def _insert_time_entry() -> dict:
20+
def _new_time_entry(time_entry: _domain.TimeEntry, database: DB):
21+
dao = infrastructure.TimeEntriesSQLDao(database)
22+
new_time_entry = dao.create(time_entry)
23+
return new_time_entry.__dict__
24+
return _new_time_entry
25+
26+
1027
def test__delete_time_entries_azure_endpoint__returns_an_time_entry_with_true_deleted__when_its_id_is_found(
11-
create_temp_time_entries,
28+
create_fake_database, time_entry_factory, insert_time_entry, create_temp_time_entries,
1229
):
30+
# fake_database = create_fake_database
31+
# existent_activity = activity_factory()
32+
# inserted_activity = insert_activity(existent_activity, fake_database)
33+
# existent_time_entry = time_entry_factory()
34+
# inserted_time_entry = insert_time_entry(existent_time_entry, fake_database)
35+
azure_time_entries._.DATABASE = create_fake_database
36+
time_entry_body = {
37+
'id': 1,
38+
'start_date': Faker().date(),
39+
'owner_id': Faker().pyint(),
40+
'description': Faker().sentence(),
41+
'activity_id': 1,
42+
'uri': Faker().uri(),
43+
'technologies': None,
44+
'end_date': Faker().date(),
45+
'deleted': False,
46+
'timezone_offset': None,
47+
'project_id': 1,
48+
}
49+
body = json.dumps(time_entry_body).encode("utf-8")
50+
req = func.HttpRequest(
51+
method='DELETE',
52+
body=body,
53+
url=TIME_ENTRY_URL,
54+
# route_params={"id": 1},
55+
)
56+
57+
response = azure_time_entries._delete_time_entry.delete_time_entry(req)
58+
assert response.get_body() == ""
59+
time_entry_json_data = json.loads(response.get_body().decode("utf-8"))
60+
# activity_body['id'] = activitiy_json_data['id']
61+
62+
# azure_time_entries._delete_time_entry.DATABASE = fake_database
63+
# req = func.HttpRequest(
64+
# method='DELETE',
65+
# body=None,
66+
# url=TIME_ENTRY_URL,
67+
# route_params={"id": inserted_time_entry["id"]},
68+
# )
69+
70+
# assert req.get_body() == ""
71+
72+
# response = azure_activities._delete_activity.delete_activity(req)
73+
# activity_json_data = json.loads(response.get_body().decode("utf-8"))
74+
75+
assert response.status_code == 200
76+
# assert activity_json_data['status'] == 0
77+
# assert activity_json_data['deleted'] is True
78+
79+
80+
81+
82+
83+
1384
time_entries_json, tmp_directory = create_temp_time_entries
1485
time_entries.delete_time_entry.JSON_PATH = tmp_directory
1586
req = func.HttpRequest(
@@ -19,6 +90,7 @@ def test__delete_time_entries_azure_endpoint__returns_an_time_entry_with_true_de
1990
route_params={"id": time_entries_json[0]["id"]},
2091
)
2192

93+
2294
response = time_entries.delete_time_entry(req)
2395
time_entry_json_data = json.loads(response.get_body().decode("utf-8"))
2496

V2/tests/conftest.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
# flake8: noqa
2-
<<<<<<< HEAD
32
from fixtures import _activity_factory, _create_fake_dao, _create_fake_database
4-
=======
53
from fixtures import create_temp_activities
64
from fixtures import create_temp_time_entries
75
from fixtures import _time_entry_factory
8-
>>>>>>> 42cfc46... feat: TT-401 validated request create time entry

V2/tests/integration/daos/time_entries_dao.test.py

Lines changed: 0 additions & 45 deletions
This file was deleted.
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import json
2+
import pytest
3+
import typing
4+
5+
from faker import Faker
6+
7+
from time_tracker.time_entries._domain import TimeEntry, TimeEntriesDao
8+
from time_tracker.time_entries._infrastructure import TimeEntriesSQLDao
9+
from time_tracker.activities._domain import Activity, ActivitiesDao
10+
11+
12+
@pytest.fixture(name='create_fake_time_entries')
13+
def _create_fake_time_entries() -> TimeEntry:
14+
def _new_time_entry(time_entry: TimeEntry, dao: TimeEntriesDao):
15+
new_time_entry = dao.create(time_entry)
16+
return new_time_entry
17+
return _new_time_entry
18+
19+
20+
def test_delete__returns_an_time_entry_with_true_deleted__when_an_time_entry_matching_its_id_is_found(
21+
create_fake_dao, create_fake_time_entries, time_entry_factory, activity_factory, insert_activity
22+
):
23+
dao = create_fake_dao
24+
25+
existent_time_entry = time_entry_factory()
26+
inserted_time_entry = create_fake_time_entries(existent_time_entry, dao)
27+
28+
29+
expected_description = Faker().sentence()
30+
time_entries_json_dao = TimeEntriesSQLDao(Faker().file_path())
31+
time_entries = create_fake_time_entries([time_entry_factory().__dict__])
32+
33+
time_entry_dto = time_entries[0]
34+
result = time_entries_json_dao.delete(time_entry_dto.id)
35+
36+
assert result.deleted is True
37+
38+
39+
def test_delete__returns_none__when_no_time_entry_matching_its_id_is_found(
40+
create_fake_time_entries,
41+
):
42+
time_entries_json_dao = TimeEntriesSQLDao(Faker().file_path())
43+
create_fake_time_entries([])
44+
45+
result = time_entries_json_dao.delete(Faker().pyint())
46+
47+
assert result is None

V2/time_tracker/_infrastructure/_db.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ def __init__(self, conn_string: str = conn_string):
1414
self.engine = sqlalchemy.create_engine(conn_string)
1515

1616
def get_session(self):
17+
self.metadata.create_all(self.engine)
1718
if self.connection is None:
18-
self.metadata.create_all(self.engine)
1919
self.connection = self.engine.connect()
2020
return self.connection

V2/time_tracker/time_entries/_application/_time_entries/_delete_time_entry.py

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,13 @@
44

55
from ... import _domain
66
from ... import _infrastructure
7+
from time_tracker._infrastructure import DB
78

8-
_JSON_PATH = (
9-
'time_tracker/time_entries/_infrastructure/_data_persistence/time_entries_data.json'
10-
)
9+
DATABASE = DB()
1110

1211

1312
def delete_time_entry(req: func.HttpRequest) -> func.HttpResponse:
14-
time_entry_dao = _infrastructure.TimeEntriesJsonDao(_JSON_PATH)
13+
time_entry_dao = _infrastructure.TimeEntriesSQLDao(DATABASE)
1514
time_entry_service = _domain.TimeEntryService(time_entry_dao)
1615
use_case = _domain._use_cases.DeleteTimeEntryUseCase(time_entry_service)
1716

@@ -21,20 +20,20 @@ def delete_time_entry(req: func.HttpRequest) -> func.HttpResponse:
2120
deleted_time_entry = use_case.delete_time_entry(time_entry_id)
2221
if not deleted_time_entry:
2322
return func.HttpResponse(
24-
body='Not found',
25-
status_code=404,
26-
mimetype="application/json"
23+
body="Not found",
24+
status_code=404,
25+
mimetype="application/json"
2726
)
2827

2928
return func.HttpResponse(
30-
body=json.dumps(deleted_time_entry.__dict__),
31-
status_code=200,
32-
mimetype="application/json"
29+
body=json.dumps(deleted_time_entry.__dict__),
30+
status_code=200,
31+
mimetype="application/json",
3332
)
3433

3534
except ValueError:
3635
return func.HttpResponse(
37-
body=b'Invalid Format ID',
38-
status_code=400,
36+
body=b"Invalid Format ID",
37+
status_code=400,
3938
mimetype="application/json"
4039
)
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
# flake8: noqa
2-
from ._data_persistence import TimeEntriesJsonDao
2+
from ._data_persistence import TimeEntriesSQLDao
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
# flake8: noqa
2-
from ._time_entries_dao import TimeEntriesJsonDao
2+
from ._time_entries_dao import TimeEntriesSQLDao
Lines changed: 41 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,52 @@
11
import json
22
import dataclasses
33
import typing
4+
import sqlalchemy
5+
import sqlalchemy.sql as sql
46

57
from time_tracker.time_entries._domain import TimeEntriesDao, TimeEntry
6-
7-
8-
class TimeEntriesJsonDao(TimeEntriesDao):
9-
10-
def __init__(self, json_data_file_path: str):
11-
self.json_data_file_path = json_data_file_path
12-
self.time_entry_key = [field.name for field in dataclasses.fields(TimeEntry)]
13-
14-
def __get_time_entries_from_file(self) -> typing.List[dict]:
15-
try:
16-
file = open(self.json_data_file_path)
17-
time_entries = json.load(file)
18-
file.close()
19-
20-
return time_entries
21-
22-
except FileNotFoundError:
23-
return []
8+
import time_tracker.time_entries._domain as domain
9+
from time_tracker._infrastructure import _db
10+
11+
12+
class TimeEntriesSQLDao(TimeEntriesDao):
13+
def __init__(self, database: _db.DB):
14+
self.time_entry_keys = [
15+
field.name for field in dataclasses.fields(domain.TimeEntry)
16+
]
17+
self.db = database
18+
self.time_entry = sqlalchemy.Table(
19+
"time_entry",
20+
self.db.metadata,
21+
sqlalchemy.Column("id", sqlalchemy.Integer, primary_key=True, autoincrement=True),
22+
sqlalchemy.Column("start_date", sqlalchemy.DateTime),
23+
sqlalchemy.Column("owner_id", sqlalchemy.Integer),
24+
sqlalchemy.Column("description", sqlalchemy.String),
25+
sqlalchemy.Column("activity_id", sqlalchemy.Integer, sqlalchemy.ForeignKey("activity.id")),
26+
sqlalchemy.Column("uri", sqlalchemy.String),
27+
sqlalchemy.Column("technologies", sqlalchemy.ARRAY(sqlalchemy.String).with_variant(sqlalchemy.String,"sqlite")),
28+
sqlalchemy.Column("end_date", sqlalchemy.DateTime),
29+
sqlalchemy.Column("deleted", sqlalchemy.Boolean),
30+
sqlalchemy.Column("timezone_offset", sqlalchemy.String),
31+
sqlalchemy.Column("project_id", sqlalchemy.Integer),
32+
extend_existing=True,
33+
)
2434

2535
def __create_time_entry_dto(self, time_entry: dict) -> TimeEntry:
26-
time_entry = {key: time_entry.get(key) for key in self.time_entry_key}
36+
time_entry = {key: time_entry.get(key) for key in self.time_entry_keys}
2737
return TimeEntry(**time_entry)
2838

29-
def delete(self, time_entry_id: int) -> TimeEntry:
30-
time_entry = {
31-
time_entry.get('id'): time_entry
32-
for time_entry in self.__get_time_entries_from_file()
33-
}.get(int(time_entry_id))
34-
35-
if time_entry:
36-
time_entry_deleted = {**time_entry, 'deleted': True}
37-
38-
time_entries_updated = list(
39-
map(
40-
lambda time_entry: time_entry
41-
if time_entry.get('id') != time_entry_id
42-
else time_entry_deleted,
43-
self.__get_time_entries_from_file(),
44-
)
45-
)
39+
def get_by_id(self, time_entry_id: int) -> domain.TimeEntry:
40+
query = sql.select(self.time_entry).where(self.time_entry.c.id == time_entry_id)
41+
time_entry = self.db.get_session().execute(query).one_or_none()
42+
return self.__create_time_entry_dto(dict(time_entry)) if time_entry else None
4643

47-
try:
48-
file = open(self.json_data_file_path, 'w')
49-
json.dump(time_entries_updated, file)
50-
file.close()
51-
52-
return self.__create_time_entry_dto(time_entry_deleted)
53-
54-
except FileNotFoundError:
55-
return None
44+
def delete(self, time_entry_id: int) -> TimeEntry:
5645

57-
else:
58-
return None
46+
query = (
47+
self.time_entry.update()
48+
.where(self.time_entry.c.id == time_entry_id)
49+
.values({"deleted": True})
50+
)
51+
self.db.get_session().execute(query)
52+
return self.get_by_id(time_entry_id)

0 commit comments

Comments
 (0)