SIENTIAPDE-1478
SIENTIAPDE-1478 Implement PI Web API response processing and metrics tracking - Added a new method in the API class to process responses from the PI Web API, validating tag writes and emitting metrics for success and errors. - Enhanced error handling for missing WebIds and tag names in responses, with appropriate logging and notifications. - Updated tests to cover various scenarios for processing PI Web API responses, ensuring robust functionality and metrics emission. - Refactored existing methods to integrate the new response processing logic, improving overall code clarity and maintainability.
This commit is contained in:
@@ -80,6 +80,7 @@ def api(mock_pi_web_api_client):
|
||||
mock_client = MagicMock()
|
||||
mock_client.write_value = AsyncMock()
|
||||
mock_client.close = MagicMock()
|
||||
mock_client.base_url = 'https://test-pi-server.com'
|
||||
mock_pi_web_api_client.return_value = mock_client
|
||||
|
||||
api_instance = API(
|
||||
@@ -92,6 +93,15 @@ def api(mock_pi_web_api_client):
|
||||
)
|
||||
api_instance.send_notification_async = AsyncMock()
|
||||
api_instance.info = MagicMock()
|
||||
api_instance.error = MagicMock()
|
||||
api_instance.emit_metric = AsyncMock()
|
||||
api_instance.get_core_labels = MagicMock(
|
||||
return_value={
|
||||
'pod_id': 'test_pod',
|
||||
'model_name': 'test_model',
|
||||
'workflow_name': 'test_workflow',
|
||||
}
|
||||
)
|
||||
return api_instance
|
||||
|
||||
|
||||
@@ -109,6 +119,12 @@ async def test_write_pi_web_api_data_success(mock_dataframe, api, base_input_dat
|
||||
|
||||
mock_dataframe.return_value = _create_mock_dataframe()
|
||||
|
||||
# Mock successful responses
|
||||
api.pi_web_api_client.write_value.side_effect = [
|
||||
{'Items': [{'WebId': 'web_id_1', 'Errors': []}, {'WebId': 'web_id_2', 'Errors': []}]},
|
||||
{'Items': [{'WebId': 'web_id_3', 'Errors': []}, {'WebId': 'web_id_4', 'Errors': []}]},
|
||||
]
|
||||
|
||||
result = await api.write_pi_web_api_data(input_data)
|
||||
|
||||
api.info.assert_called_once_with('Writing data to PI Web API...', metadata['metadata'])
|
||||
@@ -176,8 +192,9 @@ async def test_write_pi_web_api_data_prediction_error(mock_dataframe, api, base_
|
||||
async def test_write_pi_web_api_data_confidence_error(mock_dataframe, api, base_input_data):
|
||||
mock_dataframe.return_value = _create_mock_dataframe()
|
||||
|
||||
# First call succeeds, second fails
|
||||
api.pi_web_api_client.write_value.side_effect = [
|
||||
None,
|
||||
{'Items': [{'WebId': 'web_id_1', 'Errors': []}]},
|
||||
Exception('Confidence write failed'),
|
||||
]
|
||||
|
||||
@@ -214,6 +231,12 @@ async def test_write_pi_web_api_data_empty_tags(mock_dataframe, api, base_input_
|
||||
|
||||
mock_dataframe.return_value = _create_mock_dataframe()
|
||||
|
||||
# Mock empty responses
|
||||
api.pi_web_api_client.write_value.side_effect = [
|
||||
{'Items': []},
|
||||
{'Items': []},
|
||||
]
|
||||
|
||||
result = await api.write_pi_web_api_data(input_data)
|
||||
|
||||
api.pi_web_api_client.write_value.assert_has_calls(
|
||||
@@ -251,3 +274,151 @@ async def test_close(api):
|
||||
api.close()
|
||||
|
||||
api.pi_web_api_client.close.assert_called_once()
|
||||
|
||||
|
||||
@mark.asyncio
|
||||
async def test_process_pi_web_api_response_success(api):
|
||||
"""Test successful processing of PI Web API response with all tags written."""
|
||||
response_data = {
|
||||
'Items': [
|
||||
{'WebId': 'web_id_1', 'Errors': []},
|
||||
{'WebId': 'web_id_2', 'Errors': []},
|
||||
]
|
||||
}
|
||||
tags = {'tag1': 'web_id_1', 'tag2': 'web_id_2'}
|
||||
core_labels = {
|
||||
'pod_id': 'test_pod',
|
||||
'model_name': 'test_model',
|
||||
'workflow_name': 'test_workflow',
|
||||
}
|
||||
|
||||
confidence = await api.process_pi_web_api_response(
|
||||
response_data=response_data,
|
||||
tags=tags,
|
||||
core_labels=core_labels,
|
||||
metadata=metadata['metadata'],
|
||||
)
|
||||
|
||||
assert confidence == 0
|
||||
assert api.emit_metric.call_count == 2
|
||||
# Verify that emit_metric was called with correct tags structure
|
||||
call_args_list = api.emit_metric.call_args_list
|
||||
assert len(call_args_list) == 2
|
||||
# Check that all calls include core_labels and tag_name
|
||||
for call_args in call_args_list:
|
||||
assert 'tag_name' in call_args.kwargs['tags']
|
||||
assert call_args.kwargs['tags']['tag_name'] in ['tag1', 'tag2']
|
||||
|
||||
|
||||
@mark.asyncio
|
||||
async def test_process_pi_web_api_response_with_errors(api):
|
||||
"""Test processing response with errors in some tags."""
|
||||
response_data = {
|
||||
'Items': [
|
||||
{'WebId': 'web_id_1', 'Errors': ['Error writing tag']},
|
||||
{'WebId': 'web_id_2', 'Errors': []},
|
||||
]
|
||||
}
|
||||
tags = {'tag1': 'web_id_1', 'tag2': 'web_id_2'}
|
||||
core_labels = {
|
||||
'pod_id': 'test_pod',
|
||||
'model_name': 'test_model',
|
||||
'workflow_name': 'test_workflow',
|
||||
}
|
||||
|
||||
confidence = await api.process_pi_web_api_response(
|
||||
response_data=response_data,
|
||||
tags=tags,
|
||||
core_labels=core_labels,
|
||||
metadata=metadata['metadata'],
|
||||
)
|
||||
|
||||
assert confidence == PI_WEB_API_PREDICTION_ERROR_CONFIDENCE
|
||||
assert api.emit_metric.call_count == 2
|
||||
api.error.assert_any_call(
|
||||
"Error writing tag tag1:web_id_1 to PI Web API: ['Error writing tag']", metadata['metadata']
|
||||
)
|
||||
|
||||
|
||||
@mark.asyncio
|
||||
async def test_process_pi_web_api_response_missing_tags(api):
|
||||
"""Test processing response when number of written tags doesn't match expected."""
|
||||
response_data = {
|
||||
'Items': [
|
||||
{'WebId': 'web_id_1', 'Errors': []},
|
||||
]
|
||||
}
|
||||
tags = {'tag1': 'web_id_1', 'tag2': 'web_id_2'}
|
||||
core_labels = {
|
||||
'pod_id': 'test_pod',
|
||||
'model_name': 'test_model',
|
||||
'workflow_name': 'test_workflow',
|
||||
}
|
||||
|
||||
confidence = await api.process_pi_web_api_response(
|
||||
response_data=response_data,
|
||||
tags=tags,
|
||||
core_labels=core_labels,
|
||||
metadata=metadata['metadata'],
|
||||
)
|
||||
|
||||
assert confidence == PI_WEB_API_PREDICTION_ERROR_CONFIDENCE
|
||||
api.send_notification_async.assert_called_once()
|
||||
call_args = api.send_notification_async.call_args
|
||||
assert call_args.kwargs['notification_id'] == 'WRITE_PI_WEB_API_PREDICTION_ERROR'
|
||||
assert call_args.kwargs['level'] == NotificationLevel.ERROR
|
||||
|
||||
|
||||
@mark.asyncio
|
||||
async def test_process_pi_web_api_response_missing_webid(api):
|
||||
"""Test processing response when WebId is missing in response item."""
|
||||
response_data = {
|
||||
'Items': [
|
||||
{'Errors': []},
|
||||
{'WebId': 'web_id_2', 'Errors': []},
|
||||
]
|
||||
}
|
||||
tags = {'tag1': 'web_id_1', 'tag2': 'web_id_2'}
|
||||
core_labels = {
|
||||
'pod_id': 'test_pod',
|
||||
'model_name': 'test_model',
|
||||
'workflow_name': 'test_workflow',
|
||||
}
|
||||
|
||||
confidence = await api.process_pi_web_api_response(
|
||||
response_data=response_data,
|
||||
tags=tags,
|
||||
core_labels=core_labels,
|
||||
metadata=metadata['metadata'],
|
||||
)
|
||||
|
||||
assert confidence == PI_WEB_API_PREDICTION_ERROR_CONFIDENCE
|
||||
api.error.assert_any_call('The response did not contain some WebIds', metadata['metadata'])
|
||||
|
||||
|
||||
@mark.asyncio
|
||||
async def test_process_pi_web_api_response_missing_tag_name(api):
|
||||
"""Test processing response when tag name is not found for WebId."""
|
||||
response_data = {
|
||||
'Items': [
|
||||
{'WebId': 'unknown_web_id', 'Errors': []},
|
||||
]
|
||||
}
|
||||
tags = {'tag1': 'web_id_1'}
|
||||
core_labels = {
|
||||
'pod_id': 'test_pod',
|
||||
'model_name': 'test_model',
|
||||
'workflow_name': 'test_workflow',
|
||||
}
|
||||
|
||||
confidence = await api.process_pi_web_api_response(
|
||||
response_data=response_data,
|
||||
tags=tags,
|
||||
core_labels=core_labels,
|
||||
metadata=metadata['metadata'],
|
||||
)
|
||||
|
||||
assert confidence == PI_WEB_API_PREDICTION_ERROR_CONFIDENCE
|
||||
api.error.assert_any_call(
|
||||
'The response did not contain the tag name for WebId unknown_web_id', metadata['metadata']
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user