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:
@@ -1,6 +1,7 @@
|
||||
from temporalio import activity, workflow
|
||||
|
||||
with workflow.unsafe.imports_passed_through():
|
||||
import json
|
||||
import traceback
|
||||
from typing import Any
|
||||
|
||||
@@ -12,6 +13,8 @@ with workflow.unsafe.imports_passed_through():
|
||||
from sientia_do.observability.sientia_monitoring import SientiaMonitoring
|
||||
from sientia_do.repository.pi_web_api_client import PIWebAPIClient
|
||||
|
||||
from laborious import metrics
|
||||
|
||||
|
||||
PI_WEB_API_PREDICTION_ERROR_CONFIDENCE = 13
|
||||
|
||||
@@ -65,19 +68,106 @@ class API(SientiaMonitoring):
|
||||
def close(self) -> None:
|
||||
"""
|
||||
Close the PI Web API client and shutdown monitoring services.
|
||||
|
||||
This method properly closes all connections and resources associated
|
||||
with the PI Web API client and monitoring services.
|
||||
"""
|
||||
self.pi_web_api_client.close()
|
||||
SientiaMonitoring.shutdown(self)
|
||||
|
||||
async def process_pi_web_api_response(
|
||||
self,
|
||||
response_data: dict[str, Any],
|
||||
tags: dict[str, str],
|
||||
core_labels: dict[str, str],
|
||||
metadata: dict[str, Any],
|
||||
) -> int:
|
||||
"""
|
||||
Process the response data from PI Web API write operation.
|
||||
|
||||
Validates that all tags were successfully written, emits metrics for each tag
|
||||
(success or error), and returns the appropriate prediction confidence value.
|
||||
Sets error confidence if any tag write fails or if the number of written tags
|
||||
doesn't match the expected count.
|
||||
|
||||
Args:
|
||||
- response_data (dict[str, Any]): The response data from the PI Web API write operation.
|
||||
- tags (dict[str, str]): The tags that were written to the PI Web API.
|
||||
- core_labels (dict[str, str]): The core labels of the workflow execution.
|
||||
- metadata (dict[str, Any]): The metadata of the workflow execution.
|
||||
Returns:
|
||||
int: Prediction confidence value (0 for success, 13 for errors)
|
||||
"""
|
||||
|
||||
# Convert tags from name:webid to webid:name
|
||||
tags = {w: t for t, w in tags.items()}
|
||||
|
||||
tag_names = list[str](tags.values())
|
||||
|
||||
confidence = 0
|
||||
|
||||
# Evaluate response for each tag
|
||||
written_tags = []
|
||||
response_items = response_data.get('Items', [])
|
||||
for item in response_items:
|
||||
web_id = item.get('WebId')
|
||||
if not web_id:
|
||||
self.error('The response did not contain some WebIds', metadata)
|
||||
continue
|
||||
errors = item.get('Errors', [])
|
||||
tag_name = tags.get(web_id)
|
||||
if not tag_name:
|
||||
self.error(
|
||||
f'The response did not contain the tag name for WebId {web_id}', metadata
|
||||
)
|
||||
continue
|
||||
if errors:
|
||||
self.error(
|
||||
f'Error writing tag {tag_name}:{web_id} to PI Web API: {errors}', metadata
|
||||
)
|
||||
await self.emit_metric(
|
||||
metric_object=metrics.PI_WEB_API_PREDICTION_WRITTEN_ERROR_COUNT,
|
||||
tags={
|
||||
**core_labels,
|
||||
'tag_name': tag_name,
|
||||
},
|
||||
)
|
||||
confidence = PI_WEB_API_PREDICTION_ERROR_CONFIDENCE
|
||||
else:
|
||||
await self.emit_metric(
|
||||
metric_object=metrics.PI_WEB_API_PREDICTION_WRITTEN_COUNT,
|
||||
tags={
|
||||
**core_labels,
|
||||
'tag_name': tag_name,
|
||||
},
|
||||
)
|
||||
written_tags.append(tag_name)
|
||||
|
||||
if len(written_tags) != len(tag_names):
|
||||
self.error(
|
||||
f'The number of written tags does not match the number of tag names: Expected {tag_names} tags, but {written_tags} tags were written',
|
||||
metadata,
|
||||
)
|
||||
await self.send_notification_async(
|
||||
metadata=metadata,
|
||||
notification_id='WRITE_PI_WEB_API_PREDICTION_ERROR',
|
||||
message=f'The number of written tags does not match the number of tag names: Expected {tag_names} tags, but {written_tags} tags were written.\nResponse:\n {json.dumps(response_data, indent=4)}\nTags:\n {json.dumps(tags, indent=4)}',
|
||||
block='write_pi_web_api_data',
|
||||
level=NotificationLevel.ERROR,
|
||||
)
|
||||
confidence = PI_WEB_API_PREDICTION_ERROR_CONFIDENCE
|
||||
|
||||
return confidence
|
||||
|
||||
@activity.defn(name='write_pi_web_api_data')
|
||||
async def write_pi_web_api_data(self, input_data: dict[str, Any]) -> dict[Any, Any]:
|
||||
"""
|
||||
Write prediction and confidence data to PI Web API.
|
||||
|
||||
This method writes prediction values and confidence scores to PI Web API
|
||||
using configured web IDs. It handles errors gracefully by setting error
|
||||
confidence values when prediction writes fail and sending notifications
|
||||
for both prediction and confidence write errors.
|
||||
Writes prediction values and confidence scores to PI Web API using configured
|
||||
web IDs. Processes responses to validate writes and emit metrics. Handles errors
|
||||
gracefully by setting error confidence values when writes fail and sending
|
||||
notifications for both prediction and confidence write errors.
|
||||
|
||||
Args:
|
||||
input_data (dict[str, Any]): The input data containing:
|
||||
@@ -104,11 +194,16 @@ class API(SientiaMonitoring):
|
||||
prediction_tags = list[str](raw_prediction_tags.values())
|
||||
confidence_tags = list[str](raw_confidence_tags.values())
|
||||
|
||||
core_labels = {
|
||||
**self.get_core_labels(metadata),
|
||||
'url_path': f'{self.pi_web_api_client.base_url}{endpoint}',
|
||||
}
|
||||
|
||||
prediction_value = data.head(1)['prediction'].values[0]
|
||||
confidence_value = data.head(1)['prediction_confidence'].values[0]
|
||||
|
||||
try:
|
||||
await self.pi_web_api_client.write_value(
|
||||
prediction_response = await self.pi_web_api_client.write_value(
|
||||
web_ids=prediction_tags,
|
||||
value={
|
||||
'Timestamp': data.head(1)['timestamp'].values[0],
|
||||
@@ -118,6 +213,15 @@ class API(SientiaMonitoring):
|
||||
metadata=metadata,
|
||||
)
|
||||
|
||||
confidence = await self.process_pi_web_api_response(
|
||||
response_data=prediction_response,
|
||||
tags=raw_prediction_tags,
|
||||
core_labels=core_labels,
|
||||
metadata=metadata,
|
||||
)
|
||||
|
||||
data['prediction_confidence'] = confidence
|
||||
|
||||
except Exception as e:
|
||||
trace = traceback.format_exc()
|
||||
await self.send_notification_async(
|
||||
@@ -134,7 +238,7 @@ class API(SientiaMonitoring):
|
||||
return data.to_dict()
|
||||
|
||||
try:
|
||||
await self.pi_web_api_client.write_value(
|
||||
confidence_response = await self.pi_web_api_client.write_value(
|
||||
web_ids=confidence_tags,
|
||||
value={
|
||||
'Timestamp': data.head(1)['timestamp'].values[0],
|
||||
@@ -143,6 +247,14 @@ class API(SientiaMonitoring):
|
||||
endpoint=endpoint,
|
||||
metadata=metadata,
|
||||
)
|
||||
|
||||
await self.process_pi_web_api_response(
|
||||
response_data=confidence_response,
|
||||
tags=raw_confidence_tags,
|
||||
core_labels=core_labels,
|
||||
metadata=metadata,
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
trace = traceback.format_exc()
|
||||
await self.send_notification_async(
|
||||
|
||||
Reference in New Issue
Block a user