Skip to content

Commit 575ef3e

Browse files
committed
switch to saving validation errors in errors folder
1 parent c53089f commit 575ef3e

File tree

2 files changed

+69
-6
lines changed

2 files changed

+69
-6
lines changed

genie/write_invalid_reasons.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@
1414
def write(
1515
syn: synapseclient.Synapse, center_mapping_synid: str, error_tracker_synid: str
1616
):
17-
"""Write center errors to a file
17+
"""Write center errors to a file and save in the
18+
errors folder in the center's folder
1819
1920
Args:
2021
syn (synapseclient.Synapse): Synapse connection
@@ -29,7 +30,7 @@ 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+
errors_synid = center_mapping_df["errorsSynId"][
3334
center_mapping_df["center"] == center
3435
][0]
3536
with open(center + "_errors.txt", "w") as errorfile:
@@ -38,7 +39,7 @@ def write(
3839
else:
3940
errorfile.write(center_errors[center])
4041

41-
ent = synapseclient.File(center + "_errors.txt", parentId=staging_synid)
42+
ent = synapseclient.File(center + "_errors.txt", parentId=errors_synid)
4243
syn.store(ent)
4344
os.remove(center + "_errors.txt")
4445

tests/test_write_invalid_reasons.py

Lines changed: 65 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,25 @@
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
99

10+
@pytest.fixture
11+
def mock_centers_mapping():
12+
df = pd.DataFrame(
13+
{
14+
"center": ["A", "B"],
15+
"errorsSynId": ["synErrA", "synErrB"],
16+
"stagingSynId":["synStageA", "synStageB"],
17+
"inputSynId":["synInputA", "synInputB"],
18+
},
19+
)
20+
df.index = ["0_1", "1_1"]
21+
return df
22+
1023
CENTER_ERRORSDF = pd.DataFrame(
1124
{
1225
"id": ["syn1234", "syn2345"],
@@ -52,3 +65,52 @@ def test_get_center_invalid_errors(syn):
5265
assert center_invalid == {"SAGE": "errors", "TEST": "errors"}
5366
patch_query.assert_called_once_with("SELECT * FROM syn3333")
5467
assert patch_combine.call_count == 2
68+
69+
70+
def test_write_writes_no_errors_and_correct_errors_and_uses_correct_parent_ids(mock_centers_mapping):
71+
syn = mock.Mock()
72+
center_errors = {"A": "A had errors"} # no errors for center B
73+
74+
with mock.patch.object(write_invalid_reasons.extract, "get_syntabledf", return_value=mock_centers_mapping), \
75+
mock.patch.object(write_invalid_reasons, "get_center_invalid_errors", return_value=center_errors), \
76+
mock.patch.object(write_invalid_reasons.synapseclient, "File") as m_file_cls, \
77+
mock.patch.object(write_invalid_reasons.os, "remove") as m_remove:
78+
79+
# Make open() return a different handle per file so we can assert per-center writes
80+
file_handles = {}
81+
82+
def _open_side_effect(filename, mode):
83+
fh = mock.Mock(name=f"handle_{filename}")
84+
ctx = mock.Mock()
85+
ctx.__enter__ = mock.Mock(return_value=fh)
86+
ctx.__exit__ = mock.Mock(return_value=False)
87+
file_handles[filename] = fh
88+
return ctx
89+
90+
with mock.patch("builtins.open", side_effect=_open_side_effect) as m_open:
91+
# Make File() return different objects so we can assert store calls too (optional)
92+
ent_a = mock.Mock(name="ent_a")
93+
ent_b = mock.Mock(name="ent_b")
94+
m_file_cls.side_effect = [ent_a, ent_b]
95+
96+
write_invalid_reasons.write(
97+
syn=syn,
98+
center_mapping_synid="synCenterMap",
99+
error_tracker_synid="synErrTrack",
100+
)
101+
102+
# assertions: open + writes
103+
m_open.assert_any_call("A_errors.txt", "w")
104+
m_open.assert_any_call("B_errors.txt", "w")
105+
106+
file_handles["A_errors.txt"].write.assert_called_once_with("A had errors")
107+
file_handles["B_errors.txt"].write.assert_called_once_with("No errors!")
108+
109+
# assertions: correct Synapse folder IDs used (parentId)
110+
m_file_cls.assert_any_call("A_errors.txt", parentId="synErrA")
111+
m_file_cls.assert_any_call("B_errors.txt", parentId="synErrB")
112+
113+
# Synapse store + cleanup
114+
assert syn.store.call_args_list == [mock.call(ent_a), mock.call(ent_b)]
115+
m_remove.assert_any_call("A_errors.txt")
116+
m_remove.assert_any_call("B_errors.txt")

0 commit comments

Comments
 (0)