Skip to content

Commit 34c41e1

Browse files
committed
Unit test updates & black reformatting
1 parent a98529c commit 34c41e1

File tree

7 files changed

+79
-78
lines changed

7 files changed

+79
-78
lines changed

src/sasctl/pzmm/import_model.py

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -160,17 +160,16 @@ def import_model(
160160
be registered in SAS Model Manager.
161161
162162
Generation of the score code requires that the `input_data`, `predict_method`,
163-
and `output_variables` arguments are supplied. Otherwise, a warning will be
163+
and `score_metrics` arguments are supplied. Otherwise, a warning will be
164164
generated stating that no score code is being created.
165165
166166
The following are generated by this function if a path is provided in the
167167
model_files argument:
168-
* '*Score.py'
169-
The Python score code file for the model.
170-
* '*.zip'
171-
The zip archive of the relevant model files. In Viya 3.5 the Python score
172-
code is not present in this initial zip file.
173-
168+
* '*Score.py'
169+
The Python score code file for the model.
170+
* '*.zip'
171+
The zip archive of the relevant model files. In Viya 3.5 the Python
172+
score code is not present in this initial zip file.
174173
175174
Parameters
176175
----------
@@ -194,7 +193,7 @@ def import_model(
194193
sklearn.tree.DecisionTreeClassifier.predict_proba
195194
The default value is None.
196195
output_variables : string list, optional
197-
The scoring output_variables for the model. For classification models, it is
196+
The scoring score_metrics for the model. For classification models, it is
198197
assumed that the first value in the list represents the classification
199198
output. This function supports single and multi-class classification models.
200199
The default value is None
@@ -219,21 +218,21 @@ def import_model(
219218
Model details from an MLFlow model. This dictionary is created by the
220219
read_mlflow_model_file function. The default value is None.
221220
predict_threshold : float, optional
222-
The prediction threshold for normalized probability output_variables. Values
221+
The prediction threshold for normalized probability score_metrics. Values
223222
are expected to be between 0 and 1. The default value is None.
224223
target_values : list of strings, optional
225224
A list of target values for the target variable. This argument and the
226-
output_variables argument dictate the handling of the predicted values from
225+
score_metrics argument dictate the handling of the predicted values from
227226
the prediction method. The default value is None.
228227
kwargs : dict, optional
229228
Other keyword arguments are passed to the following function:
230-
* sasctl.pzmm.ScoreCode.write_score_code(...,
231-
binary_h2o_model=False,
232-
binary_string=None,
233-
model_file_name=None,
234-
mojo_model=False,
235-
statsmodels_model=False
236-
)
229+
* sasctl.pzmm.ScoreCode.write_score_code(...,
230+
binary_h2o_model=False,
231+
binary_string=None,
232+
model_file_name=None,
233+
mojo_model=False,
234+
statsmodels_model=False
235+
)
237236
238237
Returns
239238
-------
@@ -252,7 +251,7 @@ def import_model(
252251
if input_data is None or not predict_method or not output_variables:
253252
warn(
254253
"The following arguments are required for the automatic generation of "
255-
"score code: input_data, predict_method, output_variables."
254+
"score code: input_data, predict_method, score_metrics."
256255
)
257256
if isinstance(model_files, dict):
258257
zip_io_file = zm.zip_files(model_files, model_prefix, is_viya4=False)

src/sasctl/pzmm/write_json_files.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,7 @@ def write_model_properties_json(
333333
elif isinstance(target_values, list) and len(target_values) == 2:
334334
model_function = "Classification"
335335
target_level = "BINARY"
336-
target_event = target_values[0]
336+
target_event = str(target_values[0])
337337
event_prob_var = f"P_{target_values[0]}"
338338
elif isinstance(target_values, list) and len(target_values) > 2:
339339
model_function = "Classification"
@@ -377,10 +377,10 @@ def write_model_properties_json(
377377
"trainTable": train_table if train_table else "",
378378
"trainCodeType": "Python",
379379
"algorithm": model_algorithm if model_algorithm else "",
380-
"target_variable": target_variable if target_variable else "",
381-
"target_event": target_event if target_event else "",
382-
"target_level": target_level if target_level else "",
383-
"event_prob_var": event_prob_var if event_prob_var else "",
380+
"targetVariable": target_variable if target_variable else "",
381+
"targetEvent": target_event if target_event else "",
382+
"targetLevel": target_level if target_level else "",
383+
"eventProbVar": event_prob_var if event_prob_var else "",
384384
"modeler": modeler if modeler else "",
385385
"tool": "Python 3",
386386
"toolVersion": python_version,

src/sasctl/tasks.py

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -109,15 +109,15 @@ def _register_sklearn_35():
109109

110110

111111
def _register_sklearn_40(model, model_name, project_name, input_data, output_data=None):
112-
113112
# TODO: if not sklearn, raise ValueError
114113

115114
model_info = _sklearn_to_dict(model)
116115

117116
with TemporaryDirectory() as folder:
118-
119117
# Write model to a pickle file
120-
pzmm.PickleModel.pickle_trained_model(model, model_name, folder) # generates folder/name.pickle
118+
pzmm.PickleModel.pickle_trained_model(
119+
model, model_name, folder
120+
) # generates folder/name.pickle
121121

122122
# Create a JSON file containing model input fields
123123
pzmm.JSONFiles.write_var_json(input_data, is_input=True, json_path=folder)
@@ -131,27 +131,34 @@ def _register_sklearn_40(model, model_name, project_name, input_data, output_dat
131131
output_fields.columns = ["EM_CLASSIFICATION"]
132132
else:
133133
output_fields.name = "EM_CLASSIFICATION"
134-
pzmm.JSONFiles.write_var_json(output_fields, is_input=False, json_path=folder)
134+
pzmm.JSONFiles.write_var_json(
135+
output_fields, is_input=False, json_path=folder
136+
)
135137
else:
136-
pzmm.JSONFiles.write_var_json(output_data, is_input=False, json_path=folder)
138+
pzmm.JSONFiles.write_var_json(
139+
output_data, is_input=False, json_path=folder
140+
)
137141
# target_variable
138142
# target_event (e.g 1 for binary)
139143
# num_target_event
140144
# event_prob
141145

142146
# TODO: allow passing description in register_model()
143147

144-
pzmm.JSONFiles.write_model_properties_json(model_name,
145-
target_event=None,
146-
target_variable=None,
147-
num_target_categories=1,
148-
model_desc=model_info["description"],
149-
model_function=model_info["function"],
150-
model_type=model_info["algorithm"],
151-
json_path=folder
152-
)
148+
pzmm.JSONFiles.write_model_properties_json(
149+
model_name,
150+
target_values=None,
151+
target_variable=None,
152+
num_target_categories=1,
153+
model_desc=model_info["description"],
154+
model_function=model_info["function"],
155+
model_algorithm=model_info["algorithm"],
156+
json_path=folder,
157+
)
153158

154-
pzmm.JSONFiles.write_file_metadata_json(model_name, json_path=folder, is_h2o_model=False)
159+
pzmm.JSONFiles.write_file_metadata_json(
160+
model_name, json_path=folder, is_h2o_model=False
161+
)
155162

156163
predict_method = (
157164
"{}.predict_proba({})"
@@ -616,8 +623,8 @@ def publish_model(
616623
617624
See Also
618625
--------
619-
:meth:`model_management.publish_model <.ModelManagement.publish_model>`
620-
:meth:`model_publish.publish_model <.ModelPublish.publish_model>`
626+
model_management.publish_model
627+
model_publish.publish_model
621628
622629
623630
.. versionchanged:: 1.1.0
@@ -735,7 +742,7 @@ def update_model_performance(data, model, label, refresh=True):
735742
736743
See Also
737744
--------
738-
:meth:`model_management.create_performance_definition <.ModelManagement.create_performance_definition>`
745+
model_management.create_performance_definition
739746
740747
.. versionadded:: v1.3
741748

src/sasctl/utils/astore.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -311,7 +311,7 @@ def get_variable_properties(var):
311311
"interval": "",
312312
"num": "decimal",
313313
"character": "string",
314-
"varchar": "string"
314+
"varchar": "string",
315315
}
316316

317317
meta = {"name": var.Name.strip(), "length": int(var.Length)}

tests/unit/test_model_parameters.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -89,19 +89,20 @@ def test_bad_model_hyperparameters(self, bad_model):
8989
mp.generate_hyperparameters(bad_model, self.MODEL_NAME, Path(tmp_dir.name))
9090

9191
def test_update_json(self):
92-
from sasctl.pzmm.model_parameters import _update_json
92+
from sasctl.pzmm.model_parameters import ModelParameters as mp
9393

9494
# ensure that only relevant rows are added to hyperparameter json
9595

9696
input_json = copy.deepcopy(self.TESTJSON)
9797
input_kpis = copy.deepcopy(self.KPIS)
9898
assert (
99-
_update_json(self.MODELS[1]["id"], input_json, input_kpis) == self.TESTJSON
99+
mp._update_json(self.MODELS[1]["id"], input_json, input_kpis)
100+
== self.TESTJSON
100101
)
101102

102103
input_json = copy.deepcopy(self.TESTJSON)
103104
input_kpis = copy.deepcopy(self.KPIS)
104-
updated_json = _update_json(self.MODELS[0]["id"], input_json, input_kpis)
105+
updated_json = mp._update_json(self.MODELS[0]["id"], input_json, input_kpis)
105106

106107
pd.testing.assert_frame_equal(input_kpis, self.KPIS)
107108
assert "hyperparameters" in updated_json

tests/unit/test_write_json_files.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -218,17 +218,21 @@ def test_write_model_properties_json():
218218
prop_dict = jf.write_model_properties_json(
219219
model_name="Test_Model",
220220
target_variable="BAD",
221-
target_values=[4, 3, 1, 4],
221+
target_values=[4, 3, 1, 5],
222222
)
223-
assert json.loads(prop_dict["ModelProperties.json"])["target_level"] == "NOMINAL"
223+
assert json.loads(prop_dict["ModelProperties.json"])["targetLevel"] == "NOMINAL"
224224
assert json.loads(prop_dict["ModelProperties.json"])["properties"] == [
225-
{"name": "multiclass_target_events", "value": "4, 3, 1, 4", "type": "string"},
226-
{"name": "multiclass_proba_variables", "value": "A, B, C, D", "type": "string"},
225+
{"name": "multiclass_target_events", "value": "4, 3, 1, 5", "type": "string"},
226+
{
227+
"name": "multiclass_proba_variables",
228+
"value": "P_4, P_3, P_1, P_5",
229+
"type": "string",
230+
},
227231
]
228232

229233
with pytest.warns():
230234
prop_dict = jf.write_model_properties_json(
231-
model_name="Test_Model", target_variable="BAD", model_desc="a" * 1000
235+
model_name="Test_Model", target_variable="BAD", model_desc="a" * 10000
232236
)
233237
assert len(json.loads(prop_dict["ModelProperties.json"])["description"]) <= 1024
234238

tests/unit/test_write_score_code.py

Lines changed: 18 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -634,34 +634,27 @@ def test_predictions_to_metrics():
634634
"""
635635
with patch("sasctl.pzmm.ScoreCode._no_targets_no_thresholds") as func:
636636
metrics = ["Classification"]
637-
sc._predictions_to_metrics(metrics)
638-
func.assert_called_once_with("Classification", False)
637+
returns = [1]
638+
sc._predictions_to_metrics(metrics, returns)
639+
func.assert_called_once_with("Classification", returns, False)
639640

640641
with patch("sasctl.pzmm.ScoreCode._nonbinary_targets") as func:
641642
target_values = ["A", "B", 5]
642-
sc._predictions_to_metrics(metrics, target_values)
643-
func.assert_called_once_with("Classification", target_values, False)
643+
sc._predictions_to_metrics(metrics, returns, target_values)
644+
func.assert_called_once_with("Classification", target_values, returns, False)
644645

645646
with patch("sasctl.pzmm.ScoreCode._binary_target") as func:
646647
metrics = ["Classification", "Probability"]
647-
target_values = ["1"]
648-
sc._predictions_to_metrics(metrics, target_values)
649-
func.assert_called_once_with(metrics, None, False)
650-
651-
with pytest.raises(
652-
ValueError,
653-
match="For non-binary target variables, please provide at least two target "
654-
"values.",
655-
):
656-
target_values = ["2"]
657-
sc._predictions_to_metrics(metrics, target_values)
648+
target_values = ["1", "0"]
649+
sc._predictions_to_metrics(metrics, returns, target_values)
650+
func.assert_called_once_with(metrics, ["1", "0"], returns, None, False)
658651

659652
with pytest.raises(
660653
ValueError,
661654
match="A threshold was provided to interpret the prediction results, however "
662655
"a target value was not, therefore, a valid output cannot be generated.",
663656
):
664-
sc._predictions_to_metrics(metrics, predict_threshold=0.7)
657+
sc._predictions_to_metrics(metrics, returns, predict_threshold=0.7)
665658

666659

667660
def test_input_var_lists():
@@ -692,6 +685,7 @@ def test_check_viya_version(mock_version, mock_get_model):
692685
- Viya 4
693686
- No connection
694687
"""
688+
current_session(None)
695689
mock_version.return_value = None
696690
model = {"name": "Test", "id": "abc123"}
697691
with pytest.warns():
@@ -744,29 +738,28 @@ def test_write_score_code(score_code_mocks):
744738
score_code_mocks["_viya35_score_code_import"].return_value = ("MAS", "CAS")
745739
score_code_mocks["_check_valid_model_prefix"].return_value = "TestModel"
746740

741+
# No binary string or model file provided
747742
with pytest.raises(ValueError):
748743
sc.write_score_code(
749744
"TestModel",
750745
pd.DataFrame(data=[["A", 1], ["B", 2]], columns=["First", "Second"]),
751-
predict_proba,
752-
["C", "P"],
746+
[predict_proba, []],
753747
)
754748

749+
# Binary string and model file provided
755750
with pytest.raises(ValueError):
756751
sc.write_score_code(
757752
"TestModel",
758753
pd.DataFrame(data=[["A", 1], ["B", 2]], columns=["First", "Second"]),
759-
predict_proba,
760-
["C", "P"],
754+
[predict_proba, []],
761755
model_file_name="model.pickle",
762756
binary_string=b"Binary model string.",
763757
)
764758

765759
sc.write_score_code(
766760
"TestModel",
767761
pd.DataFrame(data=[["A", 1], ["B", 2]], columns=["First", "Second"]),
768-
predict_proba,
769-
["C", "P"],
762+
[predict_proba, []],
770763
model_file_name="model.pickle",
771764
)
772765
score_code_mocks["_viya4_model_load"].assert_called_once()
@@ -775,17 +768,15 @@ def test_write_score_code(score_code_mocks):
775768
sc.write_score_code(
776769
"TestModel",
777770
pd.DataFrame(data=[["A", 1], ["B", 2]], columns=["First", "Second"]),
778-
predict_proba,
779-
["C", "P"],
771+
[predict_proba, []],
780772
model_file_name="model.pickle",
781773
)
782774
score_code_mocks["_viya35_model_load"].assert_called_once()
783775

784776
output_dict = sc.write_score_code(
785777
"TestModel",
786778
pd.DataFrame(data=[["A", 1], ["B", 2]], columns=["First", "Second"]),
787-
predict_proba,
788-
["C", "P"],
779+
[predict_proba, []],
789780
binary_string=b"Binary model string.",
790781
)
791782
assert "TestModel_score.py" in output_dict
@@ -796,8 +787,7 @@ def test_write_score_code(score_code_mocks):
796787
sc.write_score_code(
797788
"TestModel",
798789
pd.DataFrame(data=[["A", 1], ["B", 2]], columns=["First", "Second"]),
799-
predict_proba,
800-
["C", "P"],
790+
[predict_proba, []],
801791
score_code_path=Path(tmp_dir.name),
802792
binary_string=b"Binary model string.",
803793
)

0 commit comments

Comments
 (0)