Skip to content

Commit 646f766

Browse files
committed
refactor(toggl): use @cache to save user_info
instead of creating it immediately as the module loads this helps in testing, allowing overriding of the TOGGL_USER_INFO env var
1 parent 6495d29 commit 646f766

File tree

2 files changed

+40
-34
lines changed

2 files changed

+40
-34
lines changed

compiler_admin/services/toggl.py

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from datetime import datetime
2+
from functools import cache
23
import io
34
import os
45
import sys
@@ -10,42 +11,47 @@
1011
from compiler_admin.services.google import user_info as google_user_info
1112
import compiler_admin.services.files as files
1213

13-
# cache of previously seen user information, keyed on email
14-
USER_INFO = files.JsonFileCache("TOGGL_USER_INFO")
15-
1614
# input columns needed for conversion
1715
TOGGL_COLUMNS = ["Email", "Project", "Client", "Start date", "Start time", "Duration", "Description"]
1816

1917
# default output CSV columns for Harvest
2018
HARVEST_COLUMNS = ["Date", "Client", "Project", "Task", "Notes", "Hours", "First name", "Last name"]
2119

2220

21+
@cache
22+
def user_info():
23+
"""Cache of previously seen user information, keyed on email."""
24+
return files.JsonFileCache("TOGGL_USER_INFO")
25+
26+
2327
def _get_first_name(email: str) -> str:
2428
"""Get cached first name or derive from email."""
25-
user = USER_INFO.get(email)
29+
info = user_info()
30+
user = info.get(email)
2631
first_name = user.get("First Name") if user else None
2732
if first_name is None:
2833
parts = email.split("@")
2934
first_name = parts[0].capitalize()
3035
data = {"First Name": first_name}
31-
if email in USER_INFO:
32-
USER_INFO[email].update(data)
36+
if email in info:
37+
info[email].update(data)
3338
else:
34-
USER_INFO[email] = data
39+
info[email] = data
3540
return first_name
3641

3742

3843
def _get_last_name(email: str):
3944
"""Get cached last name or query from Google."""
40-
user = USER_INFO.get(email)
45+
info = user_info()
46+
user = info.get(email)
4147
last_name = user.get("Last Name") if user else None
4248
if last_name is None:
4349
user = google_user_info(email)
4450
last_name = user.get("Last Name") if user else None
45-
if email in USER_INFO:
46-
USER_INFO[email].update(user)
51+
if email in info:
52+
info[email].update(user)
4753
else:
48-
USER_INFO[email] = user
54+
info[email] = user
4955
return last_name
5056

5157

tests/services/test_toggl.py

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,9 @@ def spy_files(mocker):
3434
return mocker.patch.object(compiler_admin.services.toggl, "files", wraps=files)
3535

3636

37-
@pytest.fixture(autouse=True)
38-
def mock_USER_INFO(mocker):
39-
return mocker.patch(f"{MODULE}.USER_INFO", new={})
37+
@pytest.fixture()
38+
def mock_user_info(mocker):
39+
return mocker.patch(f"{MODULE}.user_info")
4040

4141

4242
@pytest.fixture
@@ -64,68 +64,68 @@ def mock_toggl_detailed_time_entries(mock_toggl_api, toggl_file):
6464
return mock_toggl_api
6565

6666

67-
def test_get_first_name_matching(mock_USER_INFO):
68-
mock_USER_INFO["email"] = {"First Name": "User"}
67+
def test_get_first_name_matching(mock_user_info):
68+
mock_user_info.return_value = {"email": {"First Name": "User"}}
6969

7070
result = _get_first_name("email")
7171

7272
assert result == "User"
7373

7474

75-
def test_get_first_name_calcuated_with_record(mock_USER_INFO):
75+
def test_get_first_name_calcuated_with_record(mock_user_info):
7676
email = "user@email.com"
77-
mock_USER_INFO[email] = {"Data": 1234}
77+
mock_user_info.return_value = {email: {"Data": 1234}}
7878

7979
result = _get_first_name(email)
8080

8181
assert result == "User"
82-
assert mock_USER_INFO[email]["First Name"] == "User"
83-
assert mock_USER_INFO[email]["Data"] == 1234
82+
assert mock_user_info.return_value[email]["First Name"] == "User"
83+
assert mock_user_info.return_value[email]["Data"] == 1234
8484

8585

86-
def test_get_first_name_calcuated_without_record(mock_USER_INFO):
86+
def test_get_first_name_calcuated_without_record(mock_user_info):
8787
email = "user@email.com"
88-
mock_USER_INFO[email] = {}
88+
mock_user_info.return_value = {email: {}}
8989

9090
result = _get_first_name(email)
9191

9292
assert result == "User"
93-
assert mock_USER_INFO[email]["First Name"] == "User"
94-
assert list(mock_USER_INFO[email].keys()) == ["First Name"]
93+
assert mock_user_info.return_value[email]["First Name"] == "User"
94+
assert list(mock_user_info.return_value[email].keys()) == ["First Name"]
9595

9696

97-
def test_get_last_name_matching(mock_USER_INFO, mock_google_user_info):
98-
mock_USER_INFO["email"] = {"Last Name": "User"}
97+
def test_get_last_name_matching(mock_user_info, mock_google_user_info):
98+
mock_user_info.return_value = {"email": {"Last Name": "User"}}
9999

100100
result = _get_last_name("email")
101101

102102
assert result == "User"
103103
mock_google_user_info.assert_not_called()
104104

105105

106-
def test_get_last_name_lookup_with_record(mock_USER_INFO, mock_google_user_info):
106+
def test_get_last_name_lookup_with_record(mock_user_info, mock_google_user_info):
107107
email = "user@email.com"
108-
mock_USER_INFO[email] = {"Data": 1234}
108+
mock_user_info.return_value = {email: {"Data": 1234}}
109109
mock_google_user_info.return_value = {"Last Name": "User"}
110110

111111
result = _get_last_name(email)
112112

113113
assert result == "User"
114-
assert mock_USER_INFO[email]["Last Name"] == "User"
115-
assert mock_USER_INFO[email]["Data"] == 1234
114+
assert mock_user_info.return_value[email]["Last Name"] == "User"
115+
assert mock_user_info.return_value[email]["Data"] == 1234
116116
mock_google_user_info.assert_called_once_with(email)
117117

118118

119-
def test_get_last_name_lookup_without_record(mock_USER_INFO, mock_google_user_info):
119+
def test_get_last_name_lookup_without_record(mock_user_info, mock_google_user_info):
120120
email = "user@email.com"
121-
mock_USER_INFO[email] = {}
121+
mock_user_info.return_value = {email: {}}
122122
mock_google_user_info.return_value = {"Last Name": "User"}
123123

124124
result = _get_last_name(email)
125125

126126
assert result == "User"
127-
assert mock_USER_INFO[email]["Last Name"] == "User"
128-
assert list(mock_USER_INFO[email].keys()) == ["Last Name"]
127+
assert mock_user_info.return_value[email]["Last Name"] == "User"
128+
assert list(mock_user_info.return_value[email].keys()) == ["Last Name"]
129129
mock_google_user_info.assert_called_once_with(email)
130130

131131

0 commit comments

Comments
 (0)