Skip to content

Commit d7aeacd

Browse files
authored
Merge branch 'develop' into gen-2368-deprecate-meta-fusions
2 parents 9581830 + df16508 commit d7aeacd

File tree

2 files changed

+87
-11
lines changed

2 files changed

+87
-11
lines changed

genie/write_invalid_reasons.py

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,14 @@
1313

1414
def write(
1515
syn: synapseclient.Synapse, center_mapping_synid: str, error_tracker_synid: str
16-
):
17-
"""Write center errors to a file
16+
) -> None:
17+
"""Write center errors to a file called {center}_validation_errors.txt and
18+
save it to the errors folder in the center's folder
1819
1920
Args:
2021
syn (synapseclient.Synapse): Synapse connection
21-
center_mapping_synid (str): Center mapping Synapse id
22-
error_tracker_synid (str): Error tracking Synapse id
22+
center_mapping_synid (str): Center mapping table's synapse id
23+
error_tracker_synid (str): Error tracking table's synapse id
2324
2425
"""
2526
center_mapping_df = extract.get_syntabledf(
@@ -29,18 +30,19 @@ def write(
2930
center_errors = get_center_invalid_errors(syn, error_tracker_synid)
3031
for center in center_mapping_df["center"]:
3132
logger.info(center)
32-
staging_synid = center_mapping_df["stagingSynId"][
33+
center_errors_report_name = f"{center}_validation_errors.txt"
34+
errors_synid = center_mapping_df["errorsSynId"][
3335
center_mapping_df["center"] == center
3436
][0]
35-
with open(center + "_errors.txt", "w") as errorfile:
37+
with open(center_errors_report_name, "w") as errorfile:
3638
if center not in center_errors:
3739
errorfile.write("No errors!")
3840
else:
3941
errorfile.write(center_errors[center])
4042

41-
ent = synapseclient.File(center + "_errors.txt", parentId=staging_synid)
43+
ent = synapseclient.File(center_errors_report_name, parentId=errors_synid)
4244
syn.store(ent)
43-
os.remove(center + "_errors.txt")
45+
os.remove(center_errors_report_name)
4446

4547

4648
def _combine_center_file_errors(

tests/test_write_invalid_reasons.py

Lines changed: 77 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,26 @@
11
"""Test write invalid reasons module"""
2-
2+
import pandas as pd
3+
import pytest
4+
import synapseclient
35
from unittest import mock
46
from unittest.mock import patch
57

68
from genie import write_invalid_reasons
7-
import pandas as pd
8-
import synapseclient
9+
10+
11+
@pytest.fixture
12+
def mock_centers_mapping():
13+
df = pd.DataFrame(
14+
{
15+
"center": ["A", "B"],
16+
"errorsSynId": ["synErrA", "synErrB"],
17+
"stagingSynId": ["synStageA", "synStageB"],
18+
"inputSynId": ["synInputA", "synInputB"],
19+
},
20+
)
21+
df.index = ["0_1", "1_1"]
22+
return df
23+
924

1025
CENTER_ERRORSDF = pd.DataFrame(
1126
{
@@ -52,3 +67,62 @@ def test_get_center_invalid_errors(syn):
5267
assert center_invalid == {"SAGE": "errors", "TEST": "errors"}
5368
patch_query.assert_called_once_with("SELECT * FROM syn3333")
5469
assert patch_combine.call_count == 2
70+
71+
72+
def test_write_writes_no_errors_and_correct_errors_and_uses_correct_parent_ids(
73+
mock_centers_mapping,
74+
):
75+
syn = mock.Mock()
76+
center_errors = {"A": "A had errors"} # no errors for center B
77+
78+
with mock.patch.object(
79+
write_invalid_reasons.extract,
80+
"get_syntabledf",
81+
return_value=mock_centers_mapping,
82+
), mock.patch.object(
83+
write_invalid_reasons, "get_center_invalid_errors", return_value=center_errors
84+
), mock.patch.object(
85+
write_invalid_reasons.synapseclient, "File"
86+
) as m_file_cls, mock.patch.object(
87+
write_invalid_reasons.os, "remove"
88+
) as m_remove:
89+
# Make open() return a different handle per file so we can assert per-center writes
90+
file_handles = {}
91+
92+
def _open_side_effect(filename, mode):
93+
fh = mock.Mock(name=f"handle_{filename}")
94+
ctx = mock.Mock()
95+
ctx.__enter__ = mock.Mock(return_value=fh)
96+
ctx.__exit__ = mock.Mock(return_value=False)
97+
file_handles[filename] = fh
98+
return ctx
99+
100+
with mock.patch("builtins.open", side_effect=_open_side_effect) as m_open:
101+
# Make File() return different objects so we can assert store calls too (optional)
102+
ent_a = mock.Mock(name="ent_a")
103+
ent_b = mock.Mock(name="ent_b")
104+
m_file_cls.side_effect = [ent_a, ent_b]
105+
106+
write_invalid_reasons.write(
107+
syn=syn,
108+
center_mapping_synid="synCenterMap",
109+
error_tracker_synid="synErrTrack",
110+
)
111+
112+
# assertions: open + writes
113+
m_open.assert_any_call("A_validation_errors.txt", "w")
114+
m_open.assert_any_call("B_validation_errors.txt", "w")
115+
116+
file_handles["A_validation_errors.txt"].write.assert_called_once_with(
117+
"A had errors"
118+
)
119+
file_handles["B_validation_errors.txt"].write.assert_called_once_with("No errors!")
120+
121+
# assertions: correct Synapse folder IDs used (parentId)
122+
m_file_cls.assert_any_call("A_validation_errors.txt", parentId="synErrA")
123+
m_file_cls.assert_any_call("B_validation_errors.txt", parentId="synErrB")
124+
125+
# Synapse store + cleanup
126+
assert syn.store.call_args_list == [mock.call(ent_a), mock.call(ent_b)]
127+
m_remove.assert_any_call("A_validation_errors.txt")
128+
m_remove.assert_any_call("B_validation_errors.txt")

0 commit comments

Comments
 (0)